hdu4628 状态压缩搜索or状态dp

来源:互联网 发布:网红阿沁的淘宝店 编辑:程序博客网 时间:2024/06/05 10:02

题意很简单,让求一个个去回文串最少要花费几步才能消完。

题目的关键是记录状态,这道题的状态如何记录呢?可以列举出所有的回文子串,然后将回文子串一个个去掉,便得到状态,状态转移看代码。

这道题开始做的时候想到了一种搜索的做法,直接记忆化搜索+剪枝,200多ms过了,然后看了别人的做法,发现大家都是用dp做的,于是学了一个dp的代码,500多ms过的,两者各有优劣,dp代码好写,搜索速度快。

搜索:

#include <iostream>#include <cstring>#include <cstdio>using namespace std;char ch[100];int len;int status[100000];int cnt;int ans;int mark[100000];int min(int a,int b){    return a>b?b:a;}bool ok(int i)  //判断是否回文,二进制1表示有该位,0表示没有该位{    int cur=0;    char a[100];    for (int t=0;t<len;t++)    {        if (i&(1<<t))        {            a[cur++]=ch[t];        }    }    if (cur==0)        return false;    cur--;    int p=1;    for (int t=0;t<=cur-t;t++)    {        if (a[t]==a[cur-t])            continue;        p=0;        break;    }    if (p==0)        return false;    return true;}void init(){    cnt=0;    for (int i=1;i<(1<<len);i++)    //构造所有回文子串    {        if (ok(i))        {            cnt++;            status[cnt]=i;        }    }}void dfs(int now,int step){    if (now==((1<<len)-1))  //搜索到末状态    {        ans=min(ans,step);        mark[now]=ans;        return ;    }    if (step>=ans)  //当前花费已经大于搜索到的可能解,则停止搜索        return ;    if (mark[now]!=0&&step>=mark[now])  //当前状态已访问过且之前状态比当前状态更优,则停止搜索        return ;    mark[now]=step; //当前状态肯定是首次得到或者比之前更优,直接记录    for (int i=cnt;i>=1;i--)    {        if (now&status[i])      //如果当前状态和枚举的下一状态有重复的1,即已经消除的部分又要消除,不合法            continue;        if (mark[now|status[i]]!=0&&step+1>=mark[now|status[i]])  //如果当前状态已经访问过且之前状态比当前状态更优,则跳过            continue;        dfs(now|status[i],step+1);      //否则搜索下一状态    }    return ;}int main(){    int T;    cin>>T;    while (T--)    {        memset(mark,0,sizeof(mark));        scanf("%s",ch);        len=strlen(ch);        init();        ans=20;        dfs(0,0);        cout<<ans<<endl;    }}


dp:

#include <iostream>#include <cstring>#include <cstdio>using namespace std;char ch[20];int len;bool mark[100000];int dp[100000];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 (ch[i]!=ch[j])            return false;        i++;        j--;    }    return true;}int main(){    int T;    cin>>T;    while (T--)    {        scanf("%s",ch);        len=strlen(ch);        int cnt=(1<<len)-1;        memset(mark,0,sizeof(mark));        memset(dp,-1,sizeof(dp));        for (int i=0;i<=cnt;i++)            mark[i]=check(i);        dp[0]=0;        for (int i=0;i<cnt;i++) //之前状态        {            for (int t=(i+1)|i;t<=cnt;t=(t+1)|i)    //当前状态,这里用了二进制求包含i状态的状态t            {                if (mark[t^i]&&dp[i]!=-1)   //检测能否由之前状态推到当前状态                {                    if (dp[t]==-1)                        dp[t]=dp[i]+1;                    else                        dp[t]=dp[t]>dp[i]+1?dp[i]+1:dp[t];                }            }        }        cout<<dp[cnt]<<endl;    }}


 

0 0