1. 程式人生 > >POJ2279 Mr Young's Picture Permutations

POJ2279 Mr Young's Picture Permutations

block close 狀態 one 描述 eof opened .org lse

POJ2279 Mr Young‘s Picture Permutations

描述: 有N個學生合影,站成左對齊的k排,每行分別有N1,N2…NK個人,第一排站最後,第k排站之前。
學生身高依次是1…N。在合影時候要求每一排從左到右遞減,每一列從後面到前也遞減,一共有多少總方案
輸入
每組測試數據包含兩行。第一行給出行數k。

第二行包含從後到前(n1,n2,…,nk)的行的長度,作為由單個空格分隔的十進制整數。

問題數據以0結束。

N<=30, k<=5;

輸出 輸出每組數據的方案數

樣例輸入
1
30
5
1 1 1 1 1
3
3 2 1
4
5 3 3 1
5
6 5 4 3 2
2
15 15
0
樣例輸出
1
1
16
4158
141892608
9694845

  •   法一: dp

     用一個k元組來表示每一行已經確定的人數即可描述一個狀態,進行轉移即可。

  tip:從本題中可知,設計動態規劃的狀態轉移方程不一定要以如何計算出一個狀態的形式給出,也可以考慮用一個已知的狀態更新後續階段的狀態

技術分享圖片
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 using namespace std;
 5 long long dp[31][16][11][9][6];//空間註意計算,否則會開爆(31,31/2,31/3...)
 6 //
f(a1,a2,a3,a4,a5)表示第i層有a[i]個人時的方案數 7 int k,num[6]; 8 int main() 9 { 10 while(scanf("%d",&k) && k) 11 { 12 memset(dp,0,sizeof(dp)); 13 memset(num,0,sizeof(num)); 14 for(int i=1 ; i<=k ; i++) scanf("%d",&num[i]); 15 dp[0][0][0][0][0]=1; 16 for
(int i=0 ; i<=num[1] ; i++) 17 { 18 for(int j=0 ; j<=num[2] ; j++) 19 { 20 for(int k=0 ; k<=num[3] ; k++) 21 { 22 for(int l=0 ; l<=num[4] ; l++) 23 { 24 for(int p=0 ; p<=num[5] ; p++) 25 { 26 if(i+1<=num[1]) 27 dp[i+1][j][k][l][p]+=dp[i][j][k][l][p]; 28 if(j+1<=num[2]&&j<i) 29 dp[i][j+1][k][l][p]+=dp[i][j][k][l][p]; 30 if(k+1<=num[3]&&k<j&&k<i) 31 dp[i][j][k+1][l][p]+=dp[i][j][k][l][p]; 32 if(l+1<=num[4]&&l<k&&l<j&&l<i) 33 dp[i][j][k][l+1][p]+=dp[i][j][k][l][p]; 34 if(p+1<=num[5]&&p<l&&p<k&&p<j&&p<i) 35 dp[i][j][k][l][p+1]+=dp[i][j][k][l][p]; 36 37 } 38 } 39 } 40 } 41 } 42 printf("%lld\n",dp[num[1]][num[2]][num[3]][num[4]][num[5]]); 43 } 44 return 0; 45 }
View Code

  • 法二:(數學解法)楊氏矩陣和勾長公式

   轉載:巨佬博客

楊氏矩陣又叫楊氏圖表,它是這樣一個矩陣,滿足條件:
(1)如果格子(i,j)沒有元素,則它右邊和上邊的相鄰格子也一定沒有元素。
(2)如果格子(i,j)有元素a[i][j] a[i][j]a[i][j],則它右邊和上邊的相鄰格子要麽沒有元素,要麽有元素且比a[i][j] a[i][j]a[i][j]大。
1 ~ n所組成楊氏矩陣的個數可以通過下面的遞推式得到:

如圖就是n=3時的楊氏矩陣。

下面介紹一個公式,那就是著名的鉤子公式。
對於給定形狀,不同的楊氏矩陣的個數為:n!除以每個格子的鉤子長度加1的積。其中鉤子長度定義:每個格子右邊的格子數和它上邊的格子數之和。

  代碼:

技術分享圖片
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 using namespace std;
 5 
 6 inline int gcd(int a,int b)
 7 {
 8     return (b==0)?a:gcd(b,a%b);
 9 }
10 int d[100],num[100];
11 int n;
12 int main()
13 {
14     while(scanf("%d",&n)&&n)
15     {
16         int tot=0;
17         memset(num,0,sizeof(num));
18         for(int i=1 ; i<=n ; i++) scanf("%d",&d[i]);
19         for(int i=n ; i>=1 ; i--)
20             for(int j=1 ; j<=d[i] ; j++)
21             {
22                 tot++;
23                 for(int k=i+1 ; k<=n ; k++)
24                     if(d[k]>=j) num[tot]++;
25                     else break;
26                 num[tot]+=d[i]-j+1;
27             }
28         long long t1=1,t2=1;
29         for(int i=1 ; i<=tot ; i++)
30         {
31             t1*=i; t2*=num[i];
32             int t=gcd(t1,t2);
33             t1/=t,t2/=t;
34         }
35         printf("%lld\n",t1/t2);
36     }
37     return 0;
38 }
View Code

POJ2279 Mr Young's Picture Permutations