• group:状压dp,轮廓线


    神仙题。但是难得的傻孩子cbx没有喊题解,所以也就难得的自己想出来了一个如此神仙的题。

    如果是自己想的,说它神仙是不是有点不合适啊。。?

    反正的确不好像。关键就在于这个标签。颓完标签就差不多会了。

    %%%cbx那么快就想出来了。(2个小时?)

    废话多了。

    先考虑暴力。对于16的数据范围当然要考虑状压,状态表示每一个位置是否要放兵。

    我们只需要考虑左边对右边,上边对下边的贡献,最后把答案×2即可。

    然后枚举每一层的状态,逐层转移即可。

    复杂度是$O((2^{C})^2 imes C imes R)$,9e12左右

    我想到一个没什么用的优化,既然你已经知道了本层的士兵数量,那么那些状态里不合法的就不用枚举了。

    预处理一下,复杂度是$O((C_C^{C/2})^2 imes C imes R)$,极端情况3e11左右

    但是不要想了,一分也不会多的。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<vector>
     4 #include<iostream>
     5 using namespace std;
     6 int r,c,num[129],dp[2][1048577],re[1048577][17],loc[1048577][17],scnt[1048577],ANS;
     7 char s[129][18];
     8 vector<int>v[17];
     9 int cal(int ro,int lst,int tst){
    10     int ans=0;
    11     for(int i=1;i<scnt[tst];++i)if(loc[tst][i]+1==loc[tst][i+1]&&s[ro][i]==s[ro][i+1])ans++;
    12     for(int i=1;i<=c;++i)if(lst&1<<i-1&&tst&1<<i-1&&s[ro-1][re[lst][i]]==s[ro][re[tst][i]])ans++;
    13     return ans;
    14 }
    15 int main(){
    16     scanf("%d%d",&r,&c);
    17     for(int i=1;i<=r;++i)scanf("%s",s[i]+1),num[i]=strlen(s[i]+1);
    18     for(int i=0;i<1<<c;++i){
    19         int cnt=0,j=i,alm=0;
    20         while(j)j^=j&-j,cnt++;
    21         scnt[i]=cnt;
    22         v[cnt].push_back(i);
    23         for(int k=1;k<=c;++k)re[i][k]=re[i][k-1]+(i&1<<k-1?1:0);
    24         for(int k=1;k<=c;++k)if(i&1<<k-1)loc[i][++alm]=k;
    25     }//printf("%d
    ",v[1][0]);
    26     for(int i=1;i<=r;++i){
    27         memset(dp[i&1],0,sizeof dp[i&1]);
    28         for(int j=0;j<v[num[i-1]].size();++j)for(int k=0;k<v[num[i]].size();++k)
    29             dp[i&1][v[num[i]][k]]=max(dp[i&1][v[num[i]][k]],dp[i&1^1][v[num[i-1]][j]]+cal(i,v[num[i-1]][j],v[num[i]][k]));
    30     }
    31     for(int i=0;i<=v[num[r]].size();++i)ANS=max(ANS,dp[r&1][v[num[r]][i]]);
    32     printf("%d
    ",ANS<<1);//printf("%d
    ",cal(2,1,1));
    33 }
    用作对拍的T40

    复杂度的瓶颈明显就在于$C_{16}^8$或者$2^{16}$的平方上,状压肯定是少不了的但是平方不能有。

    也就是必须一次只枚举一个状态进行转移。

    找这题的特殊性质,如果依次考虑每个格子,那么dp值是否增加只与左边一位和上边的一位有关。

    所以你枚举上面的一整层是多余的。

    我们只要知道这一位自身,左边和上面是谁就好了,其余位置并不在意。

    而这一位填完之后,上面的那一位就作废了,取而代之的是这一位。。。

    所以我们的状态表示的就是当前轮廓线上的每一位有没有放数。。。

    具体实现还是比较简单的。需要修改二进制下的某一位,判断二进制下某一位左右各有几个1(知道是第几个就可以判断它到底是谁了)

    一个打成函数,一个预处理。

    复杂度$O(2^C imes C imes R)$

    注意干掉不合法的状态(一行完毕之后发现它填数的个数不够或者是多了)

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 using namespace std;
     5 char s[129][18];int n,m,dp[2][65538],cntl[65538][18],cntr[65538][18],ans,l[129];
     6 int chg(int st,int p,int w){
     7     if(!p)return st>>1<<1|w;
     8     int r=st&(1<<p)-1;
     9     st>>=p+1;st<<=1;st|=w;st<<=p;//printf("%d
    ",st);
    10     return st|r;
    11 }
    12 int main(){
    13     scanf("%d%d",&n,&m);
    14     for(int i=1;i<=n;++i)scanf("%s",s[i]+1),l[i]=strlen(s[i]+1);
    15     for(int i=0;i<1<<m;++i){
    16         for(int j=2;j<=m+1;++j)cntl[i][j]=cntl[i][j-1]+(i&1<<j-2?1:0);
    17         for(int j=m-1;j;--j)cntr[i][j]=cntr[i][j+1]+(i&1<<j?1:0);
    18     //    for(int j=1;j<=m;++j)printf("%d %d %d %d
    ",i,j,cntl[i][j],cntr[i][j]);
    19     }//return 0;
    20     int nw=1,ls=0;memset(dp[nw],0xa0,sizeof dp[nw]);dp[nw][0]=0;
    21     for(int i=1;i<=n;++i){
    22         for(int j=1;j<=m;++j){
    23             nw^=1;ls^=1;memset(dp[nw],0xa0,sizeof dp[nw]);
    24             for(int st=0;st<1<<m;++st){
    25                 char sl=s[i][cntl[st][j]],su=s[i-1][l[i-1]-cntr[st][j]],sT=s[i][cntl[st][j]+1];//printf("%d %d %d %c %c %c
    ",i,j,st,sT,sl,su);
    26                 if(!(st&1<<j-2))sl=0;if(!(st&1<<j-1))su=0;
    27                 if(sT)dp[nw][chg(st,j-1,1)]=max(dp[nw][chg(st,j-1,1)],dp[ls][st]+(sl==sT)+(sT==su));
    28                 dp[nw][chg(st,j-1,0)]=max(dp[nw][chg(st,j-1,0)],dp[ls][st]);
    29             }
    30     //        for(int s=0;s<1<<m;++s)printf("%d %d %d %d
    ",i,j,s,dp[nw][s]);
    31         }
    32         for(int st=0;st<1<<m;++st)if(cntl[st][m+1]!=l[i])dp[nw][st]=0xa0a0a0a0;
    33     //    int j=m;for(int s=0;s<1<<m;++s)printf("%d %d %d %d
    ",i,j,s,dp[nw][s]);
    34     }
    35     for(int i=0;i<1<<m;++i)ans=max(ans,dp[nw][i]);
    36     printf("%d
    ",ans*2);//printf("%d
    ",chg(1,3,1));
    37 }
    View Code

    没有cbx说的那么好写好调。

    他给出的小的容易出锅的样例:

    2 1 A A

    2 2 A A

    3 3 AB AA BA(这个是我出锅的)

  • 相关阅读:
    是河南大学的悲哀???
    装完manjaro先要卸载
    技术博客
    VIM从入门到中级教程
    HTTP中GET与POST的区别
    AngularJS 拦截器实现全局$http请求loading效果
    angular指令监听ng-repeat渲染完成后执行自定义事件方法
    icheck如何修改样式大小
    Sublime text3 代码格式化插件
    代理模式小试
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11572842.html
Copyright © 2020-2023  润新知