您的位置:首页 > 编程语言

hihoCoder 1159 扑克牌 编程之美2015初赛第二场

2015-07-28 11:22 357 查看
这一题DP的状态真难想>.<看了大神的博客想了好久才明白。

我们考虑这个process,从input一堆牌中逐个选择一张从左到右放在空位上。定义状态(a,b,c,d,z),a表示相同面值总共有1张的牌有几种,b、c、d分别表示相同面值的牌总共有2、3、4张的种类数,a、b、c、d即为四个等价类。每一种牌最多四个花色,且没有重复的牌,所以最多4个等价类。

z表示前一个位置选择的牌在当前状态下属于相同面值总共有z张的等价类。初始状态中z=0。

样例中的AC AD AS JC JD KD对应的状态为(1,1,1,0,0),如果选择了AC,下一个状态为(1,2,0,0,2)

考虑以下四个disjoint case,

case 1:当前状态选择一个相同面值只有1张的牌,那么下一个状态等价类a的元素个数少了一个,且当前选择的牌在下一个状态中属于等价类0。因为相邻牌面值不同,如果前一个位置选择的牌当前属于等价类a(上一次选择的时候有a+1张),那么现在只有a-1种选择,否则有a种选择。对应的种类数为dp[a-1,b,c,d,0]*(z==1?a-1:a)*1。

case 2:当前状态选择一个相同面值只有2张的牌,那么下一个状态等价类b的元素个数少了一个,等价类a的元素多了一个。且当前选择的牌在下一个状态中属于等价类1。因为有两种花色,最后还要乘上对应的排列数2。对应的种类数为dp[a+1,b-1,c,d,1]*(z==2?b-1:b)*2。

case 3:当前状态选择一个等价类c中的牌,对应的种类数为dp[a,b+1,c-1,d,2]*(z==3?c-1:c)*3。

case 3:当前状态选择一个等价类d中的牌,对应的种类数为dp[a,b,c+1,d-1,3]*(z==4?d-1:d)*4。

边界条件为没有牌可选时,只有当前的一种选择。

纠结了好久题目中要求对2^64取模肿么处理,肯定超过long long了,而且unsigned long long最大值是2^64-1,还以为要用大数。后来发现unsigned溢出以后不会变成负数,正好和mod 2^64的结果相同。学渣微机没学好的后果>.<

在每个test case都初始化一遍dp数组会TLE。实际上没必要每次都重新计算,因为某一状态不会受input data的变化而改变。

#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<stdlib.h>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<ctype.h>
#include<map>
#include<time.h>
#include<bitset>
#include<set>
#include<list>
using namespace std;
//hiho 1159

const int maxn=14;
int T;
int N;
//2^64=18446744073709551616;
int card[20];//num of certain cards
unsigned long long dp[maxn][maxn][maxn][maxn][5];
bool used[maxn][maxn][maxn][maxn][5];
unsigned long long ans;
int amount[5];
unsigned long long dfs(int a,int b,int c,int d,int z)
{
if(used[a][b][c][d][z]==true)
{
return dp[a][b][c][d][z];
}
if(a==0&&b==0&&c==0&&d==0)
{
used[a][b][c][d][z]=true;
return dp[a][b][c][d][z]=1;
}
unsigned long long ret=0;
if(a>0)
{
ret+=dfs(a-1,b,c,d,0)*(z==1?a-1:a);
}
if(b>0)
{
ret+=dfs(a+1,b-1,c,d,1)*(z==2?b-1:b)*2;
}
if(c>0)
{
ret+=dfs(a,b+1,c-1,d,2)*(z==3?c-1:c)*3;
}
if(d>0)
{
ret+=dfs(a,b,c+1,d-1,3)*(z==4?d-1:d)*4;
}
used[a][b][c][d][z]=true;
return dp[a][b][c][d][z]=ret;
}
int main()
{
freopen("input.txt","r",stdin);
scanf("%d",&T);
memset(dp,0,sizeof(dp));//每一个test case都计算会TLE
memset(used,false,sizeof(used));
for(int ca=1;ca<=T;ca++)
{
scanf("%d",&N);
memset(card,0,sizeof(card));
//memset(dp,0,sizeof(dp));
memset(amount,0,sizeof(amount));
//memset(used,false,sizeof(used));
ans=0;
char ch[5];
for(int i=0;i<N;i++)
{
scanf("%s",ch);
if(ch[0]>='2'&&ch[0]<='9')
{
card[ch[0]-'0']++;
}
//T J Q K A
else if(ch[0]=='T')
{
card[10]++;
}
else if(ch[0]=='J')
{
card[11]++;
}
else if(ch[0]=='Q')
{
card[12]++;
}
else if(ch[0]=='K')
{
card[13]++;
}
else if(ch[0]=='A')
{
card[14]++;
}
}
for(int i=2;i<=14;i++)
{
if(card[i]!=0)
{
amount[card[i]]++;
}
}
// cout<<amount[1]<<" "<<amount[2]<<" "<<amount[3]<<" "<<amount[4]<<endl;
ans=dfs(amount[1],amount[2],amount[3],amount[4],0);
printf("Case #%d: %llu\n",ca,ans);//I64d will lead to WA
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: