您的位置:首页 > 其它

hdu 4628 Pieces (状态压缩+二进制枚举+dp)

2013-07-31 09:51 567 查看
Sample Input

2
aa
abb




Sample Output

1
2
题意 :  给一个字符串,每次可以删除一个回文串 ,比如 axbdyhba ;可以删 abyba ,剩下 xdh  ; 问最少要删多少次能把字符串删除完 ;分析;  首先状态压缩, 因为长度只有16  ;所以顶多是2^16-1 ;每个位1表示存在字母,0表示删除了这个字母  : 则一个所有0和1组成的二进制就可以表示出所有字符串的状态  : 比如 abcde : 他的状态是 11111  , ab_d_ 的状态是11010 ,表示删除第四个和第五个 :所以2^5-1能表示出所有你的子集,也就是字符串的所有状态 .而一个二进制对应着一个整数,利用哈希表,可以记录状态 is[i]=1 ,表示这个状态是回文串,is[i]=0;表示这个状态不是回文串 ; 比如 串 acda  ,  a_da 是一个回文串,二进制位1011 ,对应的整数位11 ;所有 is[11]=1 ;当枚举出了所有的状态后,就可以dp过了  ; d[i] 表示 最少需要多少步能到达 i状态  ; 所以最终答案是, 最少需要多少步能到达全部删除完的状态,也就是 ,000000 , 即 d[0]  ;    而 d[i]是通过后面得 d[j]状态通过删除字母到达d[i]状态的, 所以j>i 并且 j表示的二进制数的1的个数一定要比i多 ,因为1的个数就代表字母的个数,  j状态要通过删除一些字母才能到达i状态,所以j的二进制里的1个数要比i多;也就是  要保证 (j|i)==j ,才能使用状态转移方程  d[i]=min(d[i],d[j]+1) ;
 这是dp部分的代码 ;但是会T ;
for(inti = S-1;i>=0;i--)
{         d[i] = INF;    for(int j = i+1;j<=S;j++)     //j要比i大    {                  if((i|j)==j)  //并且j里面的1个数要比i多                   {                        if(is[j-i]) //多出的1就是要删除的串(j-i) ,还要判断删除的串是否是回文串;                             {                                     d[i] =min(d[i],d[j]+1);                            }                   }         }}
这是优化过后的 :for(int i = S-1;i>=0;i--)
{
         d[i]= INF;
         for(intj = i+1;j<=S;j = (j+1)|i) // j = (j+1)|i能省去很多状态.
         {                        //因为每次都更新j ;j的1个数都是比i多的 ;
                   if(is[j-i])
                   {
                            d[i]= min(d[i],d[j]+1);
                   }
         }
}

AC的代码 :
#include <stdio.h>
#include <string.h>
#include <math.h>
#include<algorithm>
using namespace std;
 
const int INF = 1000000000 ;
 
char str[22];
 
int n;
 
char tmp[22];
 
int check(int s)
{
   //printf("s = %d\n",s);
   int c=0;
   for(int i=0;i<n;i++)
    {
       if((s&(1<<i))!=0)
       {
           tmp[c++] = str[i];
       }
    }
   tmp[c] = '\0';
   //puts(tmp);
   for(int i=0,j=c-1;i<j;)
    {
       if(tmp[i]!=tmp[j])
       {
           return 0;
       }
       else
       {
           i++;
           j--;
       }
    }
   return 1;
}
 
int d[1<<17];
 
int is[1<<17];
 
int main()
{
   int T;
   scanf("%d",&T);
   while(T--)
    {
      scanf("%s",str);
       n = strlen(str);
      int S = (1<<(n))-1;       //共有2^n-1 种状态
      for(int i = 0;i<=S;i++)    //二进制没举所以状态
      {
          is[i]=0;
          if(check(i)) is[i]=1;   //如果i状态是回文串,标记;
      }
      d[S] = 0;           //刚开始 ,一步没动; 
      for(int i = S-1;i>=0;i--) 
      {
          d[i] = INF;
          for(int j = i+1;j<=S;j = (j+1)|i)   
          {                                   
                if(is[j-i])
                {
                    d[i] = min(d[i],d[j]+1);
                }
          }
      }
      printf("%d\n",d[0]);
    }
   return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: