题目链接: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