您的位置:首页 > 其它

[Luogu2540][NOIP2016]斗地主增强版(搜索+DP)

2018-10-23 19:13 281 查看

增强版就是原版中两鬼不算对子的版本。

先爆搜出完所有对子,剩下的牌DP处理。

考虑每个数码的拆牌情况,最多可能被拆成5种情况:1+1+1+1,1+1+2,1+3,2+2,4。故DP状态数最多为5^13≈12e8,事实上远远不满。

而爆搜部分看上去就跑的挺快,具体复杂度玄学。

几个降低代码复杂度的方法和注意点:

1.双王不算对,2不算顺子。

2.1当14处理,2和双王单独处理。

3.对于DP状态,先拆牌后打出。

4.炸弹当三带一打。

5.DP用记忆化搜索实现,递归边界:既没有3也没有4,这样转移就只需考虑3和4的拆牌和打出了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std;

const int N=30,inf=1e9;
const int lim[4]={0,5,3,2};
int T,n,x,y,a
,b
,f



;

int calc(int a,int b,int c,int d){
if (a<0 || b<0 || c<0 || d<0) return inf;
if (!c && !d) return a+b;
if (~f[a][b][c][d]) return f[a][b][c][d];
int ans=min(calc(a+1,b,c+1,d-1),calc(a,b+2,c,d-1));//拆四
ans=min(ans,min(calc(a+3,b,c-1,d),calc(a+1,b+1,c-1,d)));//拆三
ans=min(ans,min(calc(a-2,b,c,d-1),calc(a,b-1,c,d-1))+1);//四带一
ans=min(ans,min(calc(a,b-2,c,d-1),calc(a,b,c,d-2))+1);//四带二
ans=min(ans,min(calc(a-1,b,c-1,d),calc(a,b-1,c-1,d))+1);//三带一
return f[a][b][c][d]=ans;
}

int dfs(int x){
int ans=inf;
rep(k,1,3) rep(i,3,14){
int tot=0;
rep(j,i,14) if (a[j]>=k) tot++; else break;
rep(j,i+lim[k]-1,i+tot-1){
rep(l,i,j) a[l]-=k;
ans=min(ans,dfs(x+1));
rep(l,i,j) a[l]+=k;
}
}
b[1]=b[2]=b[3]=b[4]=0;
rep(i,2,14) b[a[i]]++;
if (a[0]==2) ans=min(ans,x+calc(b[1],b[2],b[3],b[4])+1);//火箭
ans=min(ans,x+calc(b[1]+a[0],b[2],b[3],b[4]));
return ans;
}

int main(){
freopen("lord.in","r",stdin);
freopen("lord.out","w",stdout);
scanf("%d%d",&T,&n); memset(f,-1,sizeof(f));
while(T--){
memset(a,0,sizeof(a));
rep(i,1,n) scanf("%d%d",&x,&y),a[x]++;
a[14]=a[1]; printf("%d\n",dfs(0));
}
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: