吐槽一下,为啥收钱协会越来越喜欢出大模拟呢[恼火]。
目录
分析:
虽然读入了花色,但是斗地主的玩法是不需要花色的,而且题目也说了,所以只需要记录点数即可。
1.顺子
然后我们可以发现:只有顺子是需要点数的,其他的直接出就可以了。
所以我们先把顺子用DFS枚举,然后再分析其他的牌。
注意:大小王不打火箭的话就可以作为不能用到顺子的单牌。
在判断顺子这里我用了一个小技巧:对于最小的顺子,连对(双顺子)和飞机(三顺子),牌数分别是:,
和
,算出来分别是5,6,6。所以直接判断
到
的最小值乘上
是不是大于5就可以了。
void dfs(int x){
if(x>=ans)return ;
memset(c,0,sizeof(c));
for(int i=2;i<=14;i++)c[s[i]]++;//记录单牌,对子,三张,炸弹的数量
ans=min(x+f[c[1]+s[0]][c[2]][c[3]][c[4]],ans);//这里是DP待会会讲
//printf("%d %d %d %d %d %d\n",c[1]+s[0],c[2],c[3],c[4],x,ans);//调试
if(s[0]==2)ans=min(ans,x+f[c[1]][c[2]][c[3]][c[4]]+1);//这里是DP待会会讲
for(int i=3;i<=14;i++){
for(int j=i+1;j<=14;j++){
int a=1e9;
for(int k=i;k<=j;k++)a=min(a,s[k]);
if(a*(j-i+1)>=5){//这里用了一个小技巧
for(int k=i;k<=j;k++)s[k]-=a;
dfs(x+1);
for(int k=i;k<=j;k++)s[k]+=a;
}
}
}
}
2.其他牌种
首先我们可以发现其实其他的牌都是一样的,详细如下:
单牌 | 零带一 |
对子 | 零带二 |
三张 | 三带零 |
三带一 | 三带一 |
三带二 | 三带二 |
炸弹 | 四带零 |
四带二 | 四带二 |
所以我们可以用DP解决剩余的牌种了
我们如果单牌,对子,三张,炸弹各有i,j,k,l个那么DP状态就好弄了:
如果出单牌:i-1;
如果出对子:j-1;
如果出三张:k-1;
如果出炸弹:l-1;
如果出三带一:i-1,k-1;
如果出三带二:j-1,k-1;
如果出四带两单:i-2,l-1;
如果出四带二双:j-2,l-1;
如果出四带一对:j-1,l-1;(注意!!!有这种情况)
你以为这就完啦?
呵呵。
3.拆牌
我们来看以下的手牌:
333,6666,JJJJ
如果你以为是三次,那么就大错特错,真正的答案是两次。
第一次:33366
第二次:JJJJ66
所以还没完呢!!!
如果对子拆成单牌:i+2,j-1;
如果三张拆成单牌和对子:i+1,j+1,k-1;
如果炸弹拆成单牌和三张:i+1,k+1,l-1;
如果炸弹拆成两个对子:j+2,l-1;
这里补充说明一下:如果三张拆成三个单牌的话,那么跟先拆成单牌和对子对子再拆成单牌是重复的。炸弹拆成四个单牌同理。
重点!!!DP的顺序一定是从L到I,因为L加的机会最少,其他的同理。
然后别忘记初始化!!!
恶心死的代码
void calc(){
for(int l=0;l<=N;l++){
for(int k=0;k<=N;k++){
for(int j=0;j<=N;j++){
for(int i=0;i<=N;i++)f[i][j][k][l]=1e9;//别忘记初始化!!!
}
}
}
f[0][0][0][0]=0;
for(int l=0;l<=N;l++){
for(int k=0;k<=N;k++){
for(int j=0;j<=N;j++){
for(int i=0;i<=N;i++){
int cnt=1e9;
//单出
if(i>0)cnt=min(cnt,f[i-1][j][k][l]+1);//单张
if(j>0)cnt=min(cnt,f[i][j-1][k][l]+1);//对子
if(k>0)cnt=min(cnt,f[i][j][k-1][l]+1);//三张
if(l>0)cnt=min(cnt,f[i][j][k][l-1]+1);//炸弹
//带牌
if(i>0&&k>0)cnt=min(cnt,f[i-1][j][k-1][l]+1);//三带一
if(j>0&&k>0)cnt=min(cnt,f[i][j-1][k-1][l]+1);//三带二
if(i>1&&l>0)cnt=min(cnt,f[i-2][j][k][l-1]+1);//四带二单
if(j>0&&l>0)cnt=min(cnt,f[i][j-1][k][l-1]+1);//四带一对
if(j>1&&l>0)cnt=min(cnt,f[i][j-2][k][l-1]+1);//四带两对
//拆牌
if(j>0)cnt=min(cnt,f[i+2][j-1][k][l]);//对子拆成两张单
if(k>0)cnt=min(cnt,f[i+1][j+1][k-1][l]);//三张拆成一单一对
if(l>0)cnt=min(cnt,f[i+1][j][k+1][l-1]);//炸弹拆成一个三张和一个单牌
if(l>0)cnt=min(cnt,f[i][j+2][k][l-1]);//炸弹拆成两对
f[i][j][k][l]=min(f[i][j][k][l],cnt);//记录答案
}
}
}
}
}
4.本题坑点
1.读入的时候一定要把A设成14因为A是在K后面。
2.DP顺序要弄好。
3.2和王不能带进顺子(应该不会错吧)。
4.王炸不能当做对子来看!!!
5.不要忘记初始化。
5.后记
这道题我整整调了1.5小时的bug,及其考验耐心和细心,还算不错的题目了。