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

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

先爆搜出完所有对子,剩下的牌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的拆牌和打出了。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 using namespace std;
 6 
 7 const int N=30,inf=1e9;
 8 const int lim[4]={0,5,3,2};
 9 int T,n,x,y,a[N],b[N],f[N][N][N][N];
10 
11 int calc(int a,int b,int c,int d){
12     if (a<0 || b<0 || c<0 || d<0) return inf;
13     if (!c && !d) return a+b;
14     if (~f[a][b][c][d]) return f[a][b][c][d];
15     int ans=min(calc(a+1,b,c+1,d-1),calc(a,b+2,c,d-1));//拆四 
16     ans=min(ans,min(calc(a+3,b,c-1,d),calc(a+1,b+1,c-1,d)));//拆三 
17     ans=min(ans,min(calc(a-2,b,c,d-1),calc(a,b-1,c,d-1))+1);//四带一 
18     ans=min(ans,min(calc(a,b-2,c,d-1),calc(a,b,c,d-2))+1);//四带二 
19     ans=min(ans,min(calc(a-1,b,c-1,d),calc(a,b-1,c-1,d))+1);//三带一 
20     return f[a][b][c][d]=ans;
21 }
22 
23 int dfs(int x){
24     int ans=inf;
25     rep(k,1,3) rep(i,3,14){
26         int tot=0;
27         rep(j,i,14) if (a[j]>=k) tot++; else break;
28         rep(j,i+lim[k]-1,i+tot-1){
29             rep(l,i,j) a[l]-=k;
30             ans=min(ans,dfs(x+1));
31             rep(l,i,j) a[l]+=k;
32         }
33     }
34     b[1]=b[2]=b[3]=b[4]=0;
35     rep(i,2,14) b[a[i]]++;
36     if (a[0]==2) ans=min(ans,x+calc(b[1],b[2],b[3],b[4])+1);//火箭 
37     ans=min(ans,x+calc(b[1]+a[0],b[2],b[3],b[4]));
38     return ans;
39 }
40 
41 int main(){
42     freopen("lord.in","r",stdin);
43     freopen("lord.out","w",stdout);
44     scanf("%d%d",&T,&n); memset(f,-1,sizeof(f));
45     while(T--){
46         memset(a,0,sizeof(a));
47         rep(i,1,n) scanf("%d%d",&x,&y),a[x]++;
48         a[14]=a[1]; printf("%d
",dfs(0));
49     }
50     return 0;
51 }
原文地址:https://www.cnblogs.com/HocRiser/p/9838595.html