描述
一副不含王的扑克牌由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等。
输入
第一行为一个整数T,为数据组数。
之后每组数据占一行。这一行首先包含一个整数N,表示给定的牌的张数,接下来N个由空格分隔的字符串,每个字符串长度为2,表示一张牌。每组数据中的扑克牌各不相同。
输出
对于每组数据输出一行,形如"Case #X: Y"。X为数据组数,从1开始。Y为可能的方案数,由于答案可能很大,请输出模264之后的值。
数据范围
1 ≤ T ≤ 20000
小数据
1 ≤ N ≤ 5
大数据
1 ≤ N ≤ 52
样例输入
5 1 TC 2 TC TS 5 2C AD AC JC JH 4 AC KC QC JC 6 AC AD AS JC JD KD
样例输出
Case #1: 1 Case #2: 0 Case #3: 48 Case #4: 24 Case #5: 120
题解:
这个题目,首先我们把模型抽象出来,13种物品,每种物品最多会有4个,当然同种物品之间也会有区别,用n个物品排出一个序列,使得相邻的两个物品种类不相同。
当然,这个题目模型一抽象出来,就十分眼熟,当然我们先不提那个题目,我们先看一下数据范围,每个物品最多只会有四种,那么我们显然可以按照物品的剩余数量给他们进行分类。
我们设dp[a1][a2][a3][a4][last]表示剩余物品数量为1的物品种数有a1种,剩余物品数量为2的物品种数有a2种,剩余物品数量为3的物品种数有a3种,剩余物品数量为4的物品种数有a4种,上一次选择用来放在序列里面的物品种类还没有放之前还剩下的数量为last的方案数。
这个我们先考虑初始状态,显然当全部为0时,无论last为什么方案数都是1,然后我们考虑转移,这里细节还是有的,就拿3为了例子,我现在把3的一种物品用掉一个,显然剩余3类的将减1,2的种数将+1,然后我们一共有a3种物品剩余数是3,所以now+=a3*dp[a1][a2+1][a3-1][a4][3](这个是错的,等下解释),但如果last==4,也就是上一次我是拿剩余数为4的物品转移过来的,也就是说我上次在序列里放到的某一种剩余数为4的物品,并且那种物品用掉了一个所以变成了剩余数为3的物品,所以剩余数为3的物品中一定有一种是我上次放的物品,所以要把哪一种减去。所以分析到这可以写出now+=(a3-(last==4))*dp(a1,a2+1,a3-1,a4,3)。
但这个还是错的,因为即使是同一种,他们之间也是有区别的,同一种牌也有花色之分,还是拿3来说,剩余数为3的物品种类数为a3,我们但看每一种物品,他的剩余数为3,那么他一定有三种花色,也就是说,你打出一同一种牌中的一张(共同特点是剩余数都是3),放入序列,这一张牌三种可能的花色,所以总方案数要乘以3,即now+=(a3-(last==4))*dp(a1,a2+1,a3-1,a4,3)*3;。
代码:(是不是和着色方案很想啊)
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<cstring> #define ll long long #define ull unsigned long long using namespace std; int num[6],pai[25]; ull f[50][50][50][50][5]; char s[20]; ull dp(int a1,int a2,int a3,int a4,int last){ ull now=0; if((a1|a2|a3|a4)==0) return f[a1][a2][a3][a4][last]=1; if(f[a1][a2][a3][a4][last]) return f[a1][a2][a3][a4][last]; if(a1) now+=(a1-(last==2))*dp(a1-1,a2,a3,a4,1); if(a2) now+=(a2-(last==3))*dp(a1+1,a2-1,a3,a4,2)*2; if(a3) now+=(a3-(last==4))*dp(a1,a2+1,a3-1,a4,3)*3; if(a4) now+=a4*dp(a1,a2,a3+1,a4-1,4)*4; f[a1][a2][a3][a4][last]=now; return now; } int main(){ int t;cin>>t; int Case=0; while(t--){ int n; memset(f,0,sizeof(f)); memset(num,0,sizeof(num)); memset(pai,0,sizeof(pai)); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s); if(s[0]>='2'&&s[0]<='9') pai[s[0]-'0']++; else if(s[0]=='T') pai[10]++; else if(s[0]=='J') pai[11]++; else if(s[0]=='Q') pai[12]++; else if(s[0]=='K') pai[13]++; else if(s[0]=='A') pai[14]++; } for(int i=2;i<=14;i++) if(pai[i]) num[pai[i]]++; printf("Case #%d: %llu ",++Case,dp(num[1],num[2],num[3],num[4],0)); } return 0; }