题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4628
一道典型的状态压缩DP。。。也是想了好久又经过大神的提示才想出来的,马上要比赛了,然后又遇到点了情感方面的问题。。。真是作死的节奏。。。悲催的程序员命。。。不多说了,看题。。。
2 aa abb
1 2
题解:
对一个长度为len 的字符串,用0和1来表示某个字符在子串中是否存在,则总共有 1<<len种情况,我们进行预处理,即针对每种情况算出它是否是回文的,然后用一个标记数组ok[i]来存下来,之后进行DP .
1.如何进行预处理,因为我们是用2进制来表示嘛,所以
bool check(int x) { if(x==0) return true; int i=0,j=len-1; while(i<j) { while(!(x&(1<<i))) i++; while(!(x&(1<<j))) j--; if(s[i]!=s[j]) return false; i++; j--; } return true; }
例如 x 011
i=0, 001
x&i!=0 即找到了最右边那位,这个大家得在纸上算一下,然后好好理解一下,要记住我们要找的是非0位。
2.接下来如何DP呢?
对于每种字串状态i,枚举包含状态i的状态j(既i中有1的位置j也有),然后判断状态j表示的字串消除的串i^j是否是回文串,是得话就可以从状态j到达状态i
对于如何枚举包含状态i的状态j:
for(int j=i;j<2^len;j=(j+1)|i);
比如:
i:1 1 0 1 0
j;1 1 0 1 0
则j+1:1 1 0 1然后(j+1)|i就将i中第一个为0的位置变为1
然后继续(j+1)|i其实就是在原前面已变位置的前提下,如果该位置前面还有0的就变成1,否则找下一个位置变为1
DP方程为 dp[i]=min(dp[i],dp[j]+1) //j为包含i的某个状态
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; const int MAXN=(1<<16)+10; const int INF=1<<30; char s[20]; bool ok[MAXN]; int dp[MAXN]; int len,bit; bool check(int x) { if(x==0) return true; int i=0,j=len-1; while(i<j) { while(!(x&(1<<i))) i++; while(!(x&(1<<j))) j--; if(s[i]!=s[j]) return false; i++; j--; } return true; } int main() { int T; while(cin>>T) { while(T--) { cin>>s; len=strlen(s); bit=1<<len; dp[bit-1]=0; // cout<<len<<" "<<bit<<endl; memset(ok,false,sizeof(ok)); for(int i=0;i< bit;i++) { ok[i]=check(i);//改成check[i]居然也能编译通过。。。。真怀疑我是不是眼睛花了。。。 } for(int i= bit-2;i>=0;i--) { dp[i]=INF; for(int j=i;j<bit;j=(j+1)|i) { if(!ok[i^j]) continue; dp[i]=min(dp[i],dp[j]+1); } } cout<<dp[0]<<endl; } } return 0; }
hdu4628(状态压缩DP),布布扣,bubuko.com
原文地址:http://blog.csdn.net/asdfghjkl1993/article/details/25122827