• [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 }
  • 相关阅读:
    poj3041——最小点覆盖
    高斯消元
    hdu1704——floyd
    poj2594——最小路径覆盖
    POJ3020 二分图匹配——最小路径覆盖
    我的老博客
    [ZJOI2015]幻想乡战略游戏 动态点分治
    HDU 5737 Differencia set + 主席树
    HDU
    HDU
  • 原文地址:https://www.cnblogs.com/skylee03/p/8987373.html
Copyright © 2020-2023  润新知