[CF413D]2048

题目大意:
  在一个长度为$n(nle2000)$的数组中填数$2$或$4$,待所有数字全部填好后,按照类似于2048的规则向左合并。给定某些格子上的数,问在当前情况下要使得合并后的最大数超过$2^k$有几种填法。

思路:
  动态规划。
  定义一个状态为最长不上升后缀的数字和,如$(16,4,8,4,4,2)$对应的状态为$18$,因为后面这些还是有机会合并的,且合并的过程可以直接用加法代替,如$(16,4,8,4,4,2)$后面再加上一个$2$,对应的状态变为$18+2=20$。定义目标状态为$2^k$,超过这个的状态对其取$min$。用$f[i][j]$表示前$i$个格子状态为$j$的方案数,则不难得到如下转移:
  当$x=2$时,$f[i][min(j+2,2^k)]+=f[i-1][j]$;
  当$x=4$且当前最后有多余$2$时,新加进来的数不可能再和前面的合并了,故不将前面的计入状态,$f[i][4]+=f[i-1][j]$;
  当$x=4$且当前最后无多余$2$时,$f[i][min(j+4,2^k)]+=f[i-1][j]$。
  当$x$不确定时,同时进行上述两种转移即可。
  时间复杂度$O(ncdot2^k)$。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<algorithm>
 4 inline int getint() {
 5     register char ch;
 6     while(!isdigit(ch=getchar()));
 7     register int x=ch^'0';
 8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
 9     return x;
10 }
11 const int K=10,mod=1e9+7;
12 int f[2][(1<<K)+1];
13 int main() {
14     const int n=getint(),k=getint()-1;
15     for(register int i=f[0][0]=1;i<=n;i++) {
16         const int x=getint();
17         std::fill(&f[i&1][0],&f[i&1][1<<k]+1,0);
18         for(register int j=0;j<=1<<k;j++) {
19             if(x!=2) (f[i&1][j&1?2:std::min(j+2,1<<k)]+=f[(i&1)^1][j])%=mod;
20             if(x!=4) (f[i&1][std::min(j+1,1<<k)]+=f[(i&1)^1][j])%=mod;
21         }
22     }
23     printf("%d
",f[n&1][1<<k]);
24     return 0;
25 }
原文地址:https://www.cnblogs.com/skylee03/p/8987373.html