状态压缩DP总结

参考博客:https://blog.csdn.net/accry/article/details/6607703

状态压缩主要指的是用位运算代替枚举压缩DP的时间,如果某一个状态和之前状态的顺序没有关系,那么就可以将之前的选或者不选压缩到一个二进制数中。在选择第i个时枚举之前的所有可能的i-1状态,但是这i-1状态是不记录顺序的,只在i-1到i时考虑顺序,这样的话往往能够节约很多时间。

【POJ3311】Hie With The Pie

大致题意是从原点出发送披萨到n个点{pi},给了pi直接到pj的时间,求送一遍再回到原点的最短时间(每个点可以走无数次)。但是有可能绕一段路比直接去时间更短,这就需要预处理一下找到pi到pj的最短时间,因为点的个数比较少所以O(n^3)也不会超时。具体的代码没有写过,看了一下题目和题解,觉得这题还是挺经典的。想法很简单,每一个子状态就是现在所在的点和之前到过的点,用一个二进制数来表示,然后枚举在这个点之前最后到的是哪一个点,求出最短的时间。然后一些位运算的小技巧也是需要注意的:dp[S][i] =min(dp[S^(1<<(i-1))][j] + dis[j][i],dp[S][i]);用异或来表示除了i没有走过其他都和S相同的状态。

【HDU3001】Traveling

这题和上一题最大的不同在于每个点最多只可以走两遍。很自然地想到要用三进制数来压缩状态。不过三进制数好像没有直接的位运算,所以得自己写。然后看了一下大佬的代码学习了一下怎么写三进制的DP优化。算一个十进制表示的k进制数的每一位是多少就是不断把这个数%k然后/=k就可以了。把一个三进制数的每一位都记下来,然后和之前一样做就好了,判断不能走的条件是某一位不能是2可以是1。不过三进制这种自己写的显然要比位运算慢很多,但是题目数据很小,只有10所以还是能过的。

【POJ2288】Islands and Bridge

题目大意是每一个点都有权值Vi,寻找一条汉密尔顿回路使得权值最大,计算方法是把回路中每个点的权值相加,再把走的相邻两点的权值之积相加,最后如果路径上相邻的三个点Pi-2,Pi-1,Pi使得Pi-2与Pi-1之间有路径相连(可以构成三角形)就把所有满足条件的这三个点权值之积做和,三部分和相加得到最后的得分。这题其实细想并不难,基本思路也是一样,只不过这回i的权值是和前两个点有关的,这个意思就是状态要记录S,i,i-1开一个三位数组,转移的时候算一下得分就好了。

【ZOJ4257】MostPowerful  

题目大意是有n种气体{gas[i]},每两种之间反应消耗其中一种可以产生能量energy[i][j],求产生的最大能量。思路很简单每一种状态是用0表示气体还在,1表示已经用过了,枚举下一次可以用的气体,然后更新下一个状态的值。但是我刚开始想的时候考虑的是枚举达到这种状态的最后一步,i和j反应用掉了i,但是这样的话要转移还要考虑前面的状态S^(1<<i)而每一次都要在这个状态里找最后一个用的是k,然后用dp[S^(1<<i)][k]来生成dp[S][i]这样就平白无故多了一重循环,十分sb。

【POJ2411】Mondriaan'sDream   

题目大意是用1*2的小矩形填满一个大矩形,有多少种填法。大致的思路还是会的,就是看一下i-1行的状态然后枚举i行可能的状态这样就得到了在i行某种状态下的方法数了。但是怎么表示一种状态是一个很大的问题。其实很自然的就会想到用二进制来表示,把矩形分成1*1的小格每一个格子是一个数,1是填了0是没填。上一行有0的地方这一行一定是放竖着的,先填一个state0,然后枚举横着的方法state[i],state[i]&state[0]==state[0]保证一定要放的那些都放了。如果state[i]^state[0]后没有连起来的奇数的一段,那么就是合法的。这样就可以转移了。在网上找到一个很精妙的代码,大致是先把竖的放好,一个一个位置递归的填过来,每一次不填和填两个。

#include<cstdio>
#include<cstring>
long long f[30][1<<12],i,j,height,width,saya=1;
void sayatime (int i,int s1,int pos){
    if (pos==width) {f[i][s1]+=saya;return;}
    sayatime(i,s1,pos+1);
    if (pos<=width-2&&!(s1&1<<pos)&&!(s1&1<<pos+1))
        sayatime(i,s1|1<<pos|1<<pos+1,pos+2);
}
int main(){
    freopen("1.txt","r",stdin);
    while(scanf("%d%d",&height,&width),height!=0){
        memset(f,0,sizeof(f));saya=1;
        sayatime(1,0,0);
        for (i=2;i<=height;i++)
            for (j=0;j<1<<width;j++){
                if (f[i-1][j]) saya=f[i-1][j];
                else continue;
                sayatime(i,~j&((1<<width)-1),0);
            }
        printf("%lld/n",f[height][(1<<width)-1]);
    }
}


猜你喜欢

转载自blog.csdn.net/yhjpku/article/details/80828120
今日推荐