1. 程式人生 > >H - 撲克牌 hihocoder1159

H - 撲克牌 hihocoder1159

coder pre closed put type 行為 content 字符串長度 數學

H - 撲克牌

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 128000/64000 KB (Java/Others) Submit Status

Problem Description

一副不含王的撲克牌由52張牌組成,由紅桃、黑桃、梅花、方塊4組牌組成,每組13張不同的面值。現在給定52張牌中的若幹張,請計算將它們排成一列,相鄰的牌面值不同的方案數。

牌的表示方法為XY,其中X為面值,為2、3、4、5、6、7、8、9、T、J、Q、K、A中的一個。Y為花色,為S、H、D、C中的一個。如2S、2H、TD等。

Input

第一行為一個整數T,為數據組數。

之後每組數據占一行。這一行首先包含一個整數N,表示給定的牌的張數,接下來N個由空格分隔的字符串,每個字符串長度為2,表示一張牌。每組數據中的撲克牌各不相同。

1 ≤ T ≤ 2000

1 ≤ N ≤ 52

Output

對於每組數據輸出一行,形如"Case #X: Y"。X為數據組數,從1開始。Y為可能的方案數,由於答案可能很大,請輸出模264之後的值。

Sample Input

5
1 TC
2 TC TS
5 2C AD AC JC JH
4 AC KC QC JC
6 AC AD AS JC JD KD

Sample Output

Case #1: 1
Case #2: 0
Case #3: 48
Case #4: 24
Case #5: 120

技術分享
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int N=110;
 5 const int INF=0x3f3f3f3f;
 6 const int mod=1e9+7;
 7 int cas=1,T;
 8 LL dp[14][55],C[N][N],fac[N];
 9 int a[N],n;
10
void init() 11 { 12 for(int i=0;i<N;i++) 13 { 14 C[i][0]=C[i][i]=1; 15 for(int j=1;j<i;j++) C[i][j]=C[i-1][j]+C[i-1][j-1]; 16 } 17 fac[0]=1; 18 for(int i=1;i<N;i++) fac[i]=fac[i-1]*i; 19 } 20 LL Cnm(int n,int m) 21 { 22 if(m>n || n<0 || m<0) return 0; 23 return C[n][m]; 24 } 25 char s[N][3]; 26 int cmp(const int &a,const int &b) 27 { 28 return a>b; 29 } 30 int main() 31 { 32 //freopen("1.in","w",stdout); 33 //freopen("1.in","r",stdin); 34 //freopen("1.out","w",stdout); 35 init(); 36 scanf("%d",&T); 37 while(T--) 38 { 39 scanf("%d",&n); 40 for(int i=1;i<=n;i++) scanf("%s",s[i]); 41 memset(a,0,sizeof(a)); 42 for(int i=1;i<=n;i++) 43 { 44 switch(s[i][0]) 45 { 46 case T:a[10]++;break; 47 case J:a[11]++;break; 48 case Q:a[12]++;break; 49 case K:a[13]++;break; 50 case A:a[1]++;break; 51 default:a[s[i][0]-0]++;break; 52 } 53 } 54 sort(a,a+14,cmp); 55 // for(int i=0;a[i];i++) printf("%d %d\n",i,a[i]); 56 memset(dp,0,sizeof(dp)); 57 dp[0][a[0]-1]=1; 58 int sum=a[0]; 59 LL ans=dp[0][0]; 60 //dp第二維記錄有多少個同花色相鄰的 61 for(int i=1;a[i];i++) 62 { 63 for(int j=0;j<sum;j++)//多少個相鄰 64 { 65 for(int k=1;k<=a[i];k++)//a[i]分成多少部分 66 { 67 for(int l=0;l<=k;l++)//多少部分放到相鄰的中間 68 { 69 LL x=j+a[i]-k-l; 70 if(x<0) continue; 71 dp[i][x]+=Cnm(j,l) * Cnm(sum+1-j,k-l)* Cnm(a[i]-1,k-1) * dp[i-1][j] ; 72 } 73 } 74 } 75 sum+=a[i]; 76 ans=dp[i][0]; 77 } 78 for(int i=0;a[i];i++) ans=ans*fac[a[i]]; 79 printf("Case #%d: %llu\n",cas++,ans); 80 } 81 //printf("time=%.3lf\n",(double)clock()/CLOCKS_PER_SEC); 82 return 0; 83 }
solve.cpp

題解:

本題數據加強版:http://acm.hdu.edu.cn/showproblem.php?pid=4532.
這題是一道組合數學dp.
dp數組有兩維,對於dp[i][j],i表示放到第幾種花色,j記錄有多少個同花色相鄰的出現,dp[i][j]表示方案數.
dp時,對於a[i]個同一種花色,可以將其劃分為k組(將相同的a[i]個球放到k個盒子,盒子不能為空),有C(a[i]-1,k-1)種分法.
然後將k組花色按原來的順序塞到前面已經排好的方案中,這裏又有兩種情況:
1.放到普通的位置
2.放到原來同花色相鄰的中間,這樣j就減小了
假設放l組到相鄰花色中間
這裏的方案數是從j個同花色相鄰的位置中選l個位置出來,則有C(j,l)種方案。
假設前面有sum張牌,則有sum+1個位置,除去j個相鄰位置,將剩下的k-l組放進來,有C(sum+1-j,k-l)種放法
所以最終的狀態轉移為
設x為放完後的同花色相鄰,則x=j+a[i]-k-l;
dp[i][x]+=Cnm(j,l) * Cnm(sum+1-j,k-l)* Cnm(a[i]-1,k-1) * dp[i-1][j]

H - 撲克牌 hihocoder1159