zoj 3675 剪指甲 状态压缩dp

 

学习了博客  http://www.cnblogs.com/dgsrz/articles/2791363.html

 

首先把 (1<<m)-1 作为指甲没剪时的初态(全是1),dp[x]保存的就是对于每个状态,需要剪的最少次数。
当前状态x以二进制表示时,出现的1表示这一位置还留有指甲,0就是已剪去。而对于指甲钳,又可以将其以二进制表示,对于案例:****..**,不妨用11110011代替,1表示当前位置刀锋完好,0相反。于是对每一位分析:

原来指甲情况 A 指甲钳刀锋 B 最终指甲情况 Y
0 0 0
0 1 0
1 0 1
1 1 0

化简一下得到每一位上的关系:Y = A & ~B
所以递推方程就是:dp[x & (~B)] = min(dp[x & (~B)], dp[x] + 1);
问题是,指甲钳并不总是和指甲最左端对齐,所以还需要对指甲钳进行移动。反应在上式,就是对B进行左右各m次的移位操作。另外,指甲钳可以反着用,于是B还需要分正反情况考虑。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 
 6 #define INF 0x3f3f3f3f
 7 int dp[1 << 20];
 8 char s[20],rev[20];
 9 
10 int main() {
11 
12     int n,m,left,right;
13     while(~scanf("%d%s%d",&n,s,&m))
14     {
15         for(int i=0;i<n;i++)
16             rev[i]=s[n-i-1];
17         left=0; right=0;
18         for(int i=0;i<n;i++)
19         {
20             left=left<<1;  right=right<<1;
21             if(s[i]=='*')
22                 left|=1;
23             if(rev[i]=='*')
24                 right|=1;
25         }
26         for(int i=0;i<1<<m;i++)
27             dp[i]=INF;
28         dp[(1<<m)-1]=0;
29         for(int i=(1<<m)-1;i>=0;i--)
30         {
31             if(dp[i]==INF)//  由i 剪到 i&(~(left<<j)的状态  若 。。  说明这还没被剪过
32                 continue;
33             for(int j=0;j<m;j++)
34             {
35                 dp[i&(~(left<<j))]=min(dp[i&(~(left<<j))],dp[i]+1);
36                 dp[i&(~(left>>j))]=min(dp[i&(~(left>>j))],dp[i]+1);
37                 dp[i&(~(right<<j))]=min(dp[i&(~(right<<j))],dp[i]+1);
38                 dp[i&(~(right>>j))]=min(dp[i&(~(right>>j))],dp[i]+1);
39             }
40         }
41         if(dp[0]<INF)
42             printf("%d\n",dp[0]);
43         else printf("-1\n");
44     }
45     return 0;
46 }

 

posted @ 2014-05-11 22:13  galaxy77  阅读(151)  评论(0编辑  收藏  举报