• 状压DP小拼盘


    有的DP题,某一部分的状态只有两种,选或不选。

    开数组记录,代价太大,转移不方便。

    状态压缩意为,用 “0/1“ 表示 “选/不选“ 。

    把状态表示为二进制整数。

    There are 10 kinds of people in the world, who knows binary and who doesn't.

    用位运算判断条件并转移状态。

    hdu 6149 Valley Numer II

    题目传送门

    用f[i][j]表示选到前i个点,状态为j的答案。

    枚举其他两个高点。

    转移之前判断之前是否用过,以及高低点之间是否有连边。

    所以用邻接矩阵表示连边比较方便。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 
     6 int t;
     7 int n,m,k;
     8 int c[35][35];
     9 int h[35];
    10 int v[35];
    11 int f[35][(1<<16)+1];
    12 int ans;
    13 
    14 int main()
    15 {
    16     scanf("%d",&t);
    17     while(t--)
    18     {
    19         memset(f,0,sizeof(f));
    20         memset(c,0,sizeof(c));
    21         memset(h,0,sizeof(h));
    22         memset(v,0,sizeof(v));
    23         scanf("%d%d%d",&n,&m,&k);
    24         for(int i=1;i<=m;i++)
    25         {
    26             int q,w;
    27             scanf("%d%d",&q,&w);
    28             c[q][w]=1;
    29             c[w][q]=1;
    30         }
    31         for(int i=0;i<k;i++)
    32         {
    33             scanf("%d",&h[i]);
    34             v[h[i]]=1;
    35         }
    36         int st=(1<<k);
    37         ans=0;
    38         for(int i=1;i<=n;i++)
    39         {
    40             for(int j=0;j<st;j++)f[i][j]=f[i-1][j];
    41             if(v[i])continue;
    42             for(int j=0;j<st;j++)
    43             {
    44                 for(int q1=0;q1<k;q1++)
    45                 {
    46                     if(j&(1<<q1))continue;
    47                     if(!c[i][h[q1]])continue;
    48                     for(int q2=0;q2<q1;q2++)
    49                     {
    50                         if(j&(1<<q2))continue;
    51                         if(!c[i][h[q2]])continue;
    52                         f[i][(j|(1<<q1))|(1<<q2)]=max(f[i][(j|(1<<q1))|(1<<q2)],f[i-1][j]+1);
    53                     }
    54                 }
    55             }
    56         }
    57         for(int j=0;j<st;j++)ans=max(ans,f[n][j]);
    58         printf("%d
    ",ans);
    59     }
    60     return 0;
    61 }
    hdu 6149 Valley Numer II

    CodeForces 895C Square Subsets

    题目传送门

    一道数学思想浓重的状压DP。

    因为1~70只有19个质数(用pr[]储存每个质数),先预处理出每个数的质因子组成c。

    c[i]的第k位表示i这个数含有几个pr[k]因子。含有奇数个,那一位就是1,,否则为零。

    f[i][j]表示选到第i个数,此时所有选的数的积的质因子组成为j的时候的方案数。

    那个“组成”与之前预处理的表示方法类似,每一个二进制位代表一个质因子的情况,0为偶数个,1为奇数个。

    读入的时候记录每个数被读进来的次数。

    如果读入的数据中含有x这个数,就对x进行一次计算。

    具体来说,x可以选奇数个也可以选偶数个。

    偶数个的情况状态不变。奇数个的情况,状态异或上x这个数的组成c[x]。

    两个加一起,乘上方案数,取个模即可。

    最后因为要求是平方数,所以所有质因子都有偶数个,状态为零。

    输出f[nw][0]。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define mod 1000000007
     5 #define ll long long
     6 using namespace std;
     7 
     8 int n;
     9 int h[75];
    10 int c[75];
    11 int pr[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71};
    12 ll f[2][1<<20];
    13 ll pow[100005];
    14 int main()
    15 {
    16     scanf("%d",&n);
    17     for(int i=1;i<=n;i++)
    18     {
    19         int t;
    20         scanf("%d",&t);
    21         h[t]++;
    22     }
    23     for(int i=1;i<=70;i++)
    24     {
    25         int t=i;
    26         int p=0;
    27         while(t>1)
    28         {
    29             while(t%pr[p]==0)
    30             {
    31                 t/=pr[p];
    32                 c[i]^=(1<<p);
    33             }
    34             p++;
    35         }
    36     }
    37     pow[0]=1;
    38     for(int i=1;i<=n;i++)pow[i]=(pow[i-1]<<1)%mod;
    39     f[0][0]=1;
    40     int nw=0;
    41     for(int i=1;i<=70;i++)
    42     {
    43         if(!h[i])continue;
    44         nw^=1;
    45         for(int j=0;j<(1<<19);j++)
    46         {
    47             f[nw][j]=(f[nw^1][j^c[i]]+f[nw^1][j])%mod*pow[h[i]-1]%mod;
    48         }
    49     }
    50     printf("%d",f[nw][0]-1);
    51     return 0;
    52 }
    CodeForces 895C Square Subsets

    CodeForces 482C Game with Strings

    题目传送门

    一道期望+状压DP......

    摧残大脑的推导与状态转移。

    强荐zhx大佬的题解,写的非常非常明白。zhx大佬的题解传送门

    作为蒟蒻我就只能大致解释解释代码了QwQ

    f[i]代表问了状态为i的问题时,距离确定字符串(终点结果)的期望次数。

    显然,f[一个能确定字符串的问法]=0。

    接下来的难点在于如何判断每种问法不能确定哪些字符串。

    设dbt[i]代表问法为i时,不能区分的字符串有哪些。

    用二进制数表示,每一位代表一个字符串,1代表不能确定,0反之。

    num[i]代表问法为i时,不能区分的串的个数,也就是dbt[i]中1的个数。

    接下来玄学公式和位运算转移状态,zhx大佬的题解里写的很详细,本蒟蒻在此不做赘述。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define ll long long
     5 using namespace std;
     6 
     7 int n,m;
     8 char s[55][25];
     9 ll dbt[1<<20];
    10 int num[1<<20];
    11 double f[1<<20];
    12 
    13 int main()
    14 {
    15     scanf("%d",&n);
    16     for(int i=0;i<n;i++)
    17     {
    18         scanf("%s",s[i]);
    19     }
    20     if(n==1){printf("0.000000000");return 0;}
    21     m=strlen(s[1]);
    22     for(int i=0;i<n;i++)
    23     {
    24         for(int j=i+1;j<n;j++)
    25         {
    26             ll st=0;
    27             for(int k=0;k<m;k++)
    28             {
    29                 if(s[i][k]==s[j][k])st|=(1ll<<k);
    30             }
    31             dbt[st]|=(1ll<<i);
    32             dbt[st]|=(1ll<<j);
    33         }
    34     }
    35     for(int i=(1<<m)-1;i>=1;i--)
    36     {
    37         for(int j=0;j<m;j++)
    38         {
    39             if(i&(1<<j))dbt[i^(1<<j)]|=dbt[i];
    40         }
    41     }
    42     for(int i=0;i<(1<<m);i++)
    43     {
    44         for(int j=0;j<n;j++)
    45         {
    46             if(dbt[i]&(1ll<<j))num[i]++;
    47         }
    48     }
    49     f[(1<<m)-1]=0.00;
    50     for(int i=(1<<m)-2;i>=0;i--)
    51     {
    52         if(!num[i]){f[i]=0.00;continue;}
    53         int tot=m;
    54         for(int j=0;j<m;j++)
    55         {
    56             if(i&(1<<j))tot--;
    57         }
    58         for(int j=0;j<m;j++)
    59         {
    60             if(i&(1<<j))continue;
    61             f[i]+=f[i|(1<<j)]*1.0/(double)(tot)*(double)(num[i|(1<<j)])/(double)(num[i]);
    62         }
    63         f[i]+=1.00;
    64     }
    65     printf("%.9lf",f[0]);
    66     return 0;
    67 }
    CodeForces 482C Game with Strings

    hdu 6125 Free from square

    题目传送门

    由于对于一个合法的答案,sqrt(500)后的素数最多只会用到一个,所以只对前8个素数状压后01背包。 

    f(S, k)表示素数状态为S时由k个自然数组成的方法有几种。 

    再对后面的素数分别01背包。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define mod 1000000007
     5 #define ll long long
     6 using namespace std;
     7 
     8 int t,n,K;
     9 int num[1<<8];
    10 bool pr[505];
    11 ll f[1<<8][505];
    12 ll ans;
    13 
    14 int main()
    15 {
    16     num[0]=1;
    17     int k=0;
    18     for(int i=2;i<505;i++)
    19     {
    20         if(!pr[i])
    21         {
    22             if(k<8)num[1<<k]=i,k++;
    23             for(int j=i<<1;j<505;j+=i)pr[j]=1;
    24         }
    25     }
    26     for(int i=1;i<(1<<8);i++)
    27     {
    28         int j=(i-1)&i;
    29         num[i]=num[j]*num[i^j];
    30         if(num[i]>505)num[i]=505;
    31     }
    32     scanf("%d",&t);
    33     while(t--)
    34     {
    35         scanf("%d%d",&n,&K);
    36         memset(f,0,sizeof(f));
    37         f[0][0]=1;
    38         for(int i=0;i<(1<<8);i++)
    39         {
    40             if(num[i]<=n)
    41             {
    42                 int ss =((1<<8)-1)^i;
    43                 for(int k=K;k>=1;k--)
    44                 for(int j=ss;;j=(j-1)&ss)
    45                 {
    46                     f[i|j][k]=(f[i|j][k]+f[j][k-1])%mod;
    47                     if(j==0)break;
    48                 }
    49                }
    50         }
    51         for(int i=23;i<=n;i++)
    52         {
    53             if(pr[i])continue;
    54             for(int k=K;k>0;k--)
    55             {
    56                 for(int j=0;j<(1<<8);j++)
    57                 {
    58                     if(num[j]*i<=n)
    59                     {
    60                         int ss=((1<<8)-1)^j;
    61                         for(int s=ss;;s=(s-1)&ss)
    62                         {
    63                             f[j|s][k]=(f[s][k-1]+f[j|s][k])%mod;
    64                             if(s==0)break;
    65                         }
    66                     }
    67                 }
    68             } 
    69         }
    70         ans=0;
    71         for(int i=0;i<(1<<8);i++)
    72             for(int k=K;k>=1;k--)
    73                 ans=(ans+f[i][k])%mod;
    74         printf("%lld
    ",ans);
    75     }
    76     return 0;
    77 }
    hdu 6125 Free from square

    CodeForces 449D Jzzhu and Numbers

    题目传送门

    传说中的官方题解:

    (如果图看起来长宽比不对,查看原图即可)

    想法不是很简单。

    脑洞大,真的想不出来。

    看了题解瞎打了一会,cf测,过不了test5。

    盯着code看了半个小时,发现是a数组开小了,少打了个0。

    话说a数组本来也没什么用是吧。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define ll long long
     5 #define mod 1000000007
     6 using namespace std;
     7 
     8 int n;
     9 int a[1000005];
    10 int f[1<<20][20];
    11 
    12 ll ksm(int B,int p)
    13 {
    14     ll ret=1;
    15     ll b=B;
    16     while(p)
    17     {
    18         if(p&1)ret=(ret*b)%mod;
    19         b=(b*b)%mod;
    20         p>>=1;
    21     }
    22     return ret;
    23 }
    24 
    25 int cnt(int x)
    26 {
    27     int ret=0;
    28     while(x)x-=(x&(-x)),ret++;
    29     return (ret&1)?-1:1;
    30 }
    31 
    32 int main()
    33 {
    34     scanf("%d",&n);
    35     for(int i=1;i<=n;i++)scanf("%d",&a[i]),f[a[i]][0]++;
    36     for(int i=1;i<=20;i++)
    37     {
    38         for(int j=0;j<(1<<20);j++)
    39         {
    40             if(1&(j>>(i-1)))f[j][i]=f[j][i-1];
    41             else f[j][i]=f[j][i-1]+f[j+(1<<(i-1))][i-1];
    42         }
    43     }
    44     ll ans=0;
    45     for(int i=0;i<(1<<20);i++)
    46     {
    47         ll tmp=((ksm(2,f[i][20])-1)*cnt(i)%mod+mod)%mod;
    48         ans=(ans+tmp+mod)%mod;
    49     }
    50     printf("%I64d",ans);
    51     return 0;
    52 }
    CodeForces 449D Jzzhu and Numbers

    hdu 5713 K个联通块

    题目传送门

    跟前面某些题相比还是比较友善的。

    状态不是很难想,转移也是显然,就是中间的步骤比较多,倒起来有些复杂。

    首先算出num[i]表示选点为i状态时,i中这些点相互连边的方案数。

    具体做法是找出lowbit代表的那个点,跟前面的点一一计算累加到答案上。

    再算出f[i][p]代表所选点集为i时,构成p个联通块的方案数。

    第一步要算出f[i][1]。

    利用容斥原理,用所有的方法减去不合法的方法。

    再利用f[i][1]算出f[i][2...k]。

    最后的答案为f[所有点都选((1<<n)-1)][k]。

    注意取模。

    模数很奇葩。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define ll long long
     5 #define mod 1000000009
     6 using namespace std;
     7 
     8 int t,n,m,k;
     9 int e[20][20],num[1<<14];
    10 ll f[1<<14][20];
    11 
    12 int count(int nw,int p)
    13 {
    14     int ret=0;
    15     for(int i=1;i<=n;i++)
    16     {
    17         if((1<<(i-1))!=p)continue;
    18         p=i;
    19         break;
    20     }
    21     for(int i=1;i<=n;i++)
    22     {
    23         if(nw&(1<<(i-1)))ret+=e[i][p];
    24     }
    25     return ret;
    26 }
    27 
    28 int main()
    29 {
    30     scanf("%d",&t);
    31     for(int cs=1;cs<=t;cs++)
    32     {
    33         memset(e,0,sizeof(e));
    34         memset(num,0,sizeof(num));
    35         memset(f,0,sizeof(f));
    36         scanf("%d%d%d",&n,&m,&k);
    37         for(int i=1;i<=m;i++)
    38         {
    39             int v1,v2;
    40             scanf("%d%d",&v1,&v2);
    41             e[v1][v2]=e[v2][v1]=1;
    42         }
    43         for(int i=1;i<(1<<n);i++)
    44         {
    45             int lb=i&(-i);
    46             num[i]=num[i^lb]+count(i,lb);
    47         }
    48         for(int i=1;i<(1<<n);i++)
    49         {
    50             int lb=i&(-i);
    51             ll no=0;
    52             for(int j=i^lb;j;j=((j-1)&(i^lb)))
    53             {
    54                 no=(no+f[i^j][1]*1ll%mod*(1ll<<num[j])%mod)%mod;
    55             }
    56             f[i][1]=(1ll*(1ll<<num[i])%mod-no)%mod;
    57         }
    58         for(int i=1;i<(1<<n);i++)
    59         {
    60             for(int j=2;j<=k;j++)
    61             {
    62                 int lb=i&(-i);
    63                 for(int h=i^lb;h;h=((h-1)&(i^lb)))
    64                 {
    65                     f[i][j]=(f[i][j]+f[h][j-1]*f[i^h][1]%mod)%mod;
    66                 }
    67             }
    68         }
    69         printf("Case #%d:
    %lld
    ",cs,(f[(1<<n)-1][k]%mod+mod)%mod);
    70     }
    71     return 0;
    72 }
    hdu 5713 K个联通块
  • 相关阅读:
    Vue 左右翻页,点赞动画
    gitbook 使用
    css3 序列帧动画抖动
    ios添加-webkit-overflow-scrolling依然卡顿
    css 多行省略号兼容移动端
    Vue粒子特效(vue-particles插件)
    css3 渐变色兼容移动端
    前端性能优化:客户端从输入到展示讲解
    前端通信:ajax设计方案(六)--- 全局配置、请求格式拓展和优化、请求二进制类型、浏览器错误搜集以及npm打包发布
    前端通信:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码(改迭代已作废,移步迭代10)
  • 原文地址:https://www.cnblogs.com/cervusy/p/9487868.html
Copyright © 2020-2023  润新知