POJ1699 Best Sequence 暨2013年USC校赛D题

 
今晚有点失望,但是也是自己作祟,不能对自己喜欢的girl有太多的要求,至少不能想要求自己一样....因为严于律己宽于待人嘛~~~ 
题目思路:给出n(n<=10)个字符串,求出这些字符串在某种排列之后形成的(重叠的可以减去)新串最短
 
思路:这个题目爆搜可以求解,但是最近做了道状态压缩DP,所以坚持用状态压缩DP去做了,但是这个题目描述真的是很坑爹,注意一点就可以了:是不能存在包含在内的情况出现,必须是首尾相连的,考虑到这一点就基本可以了.
比如这组数据:
2
ACCA
CC
输出6才AC.至少我的改为6才AC
dp[i][k]表示的是在i状态下以k字符串结尾的新串最短长度.
注意之所以要考虑到还要枚举k这个状态,因为k这个最后的字符串对下一个匹配是有影响的.
 
 
我的字符串预处理只是简单模拟:pre[i][j]表示的是i,j字符串这样的排列顺序,他们的最大公共子串(首尾相连的情况下).
这道题有两个小小的trick:首先是字符串预处理,这里可以为多组数据省下不少时间,另外就是dp的边界数组初始化好很重要,比如这道题的对于每个字符串本身,以自己为尾串的最短长度是本身.
其实这道题:队长ZZY还有思维帝军哥给我指出了一点,其实如果能够有字符串包含的情况的话,就绝不能用我这样的状态方程来解决了.
比如这组数据:
4
G
T
C
GTC.
在这道题的意思是4,但是如果能够包含的话就是3了.
为什么不能用我这个dp来做呢,因为我的状态方程只是与尾字符串有关系,但是如果当前所求的情况与前面的字符串都有关系的话,也就是当前的状态必须要考虑到前面的所有状态,那么这种状态我的状态方程明显没有包含.
 
AC Program(无注释版):
 
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm> 
#include<string.h>
#include<map>
#define inf 10000000
using namespace std;
int vis[1050];
string mm[12];
int pre[12][12];
int length[12];
int dp[1050][12];
int fn(string s1 ,string s2)
{
     int len1=s1.length();
     int len2=s2.length();
     for(int i=len2;i>=1;i--)  
     {
          if((len1-i)<0)continue;
          string t1=s1.substr(len1-i);
          string t2=s2.substr(0,i);
          if(t1.find(t2,0)==0)
            {
                return i; 
            }       
     }
     return 0;
}
int main()
{
int test,n,state;
cin>>test;
while(test--)
{
   
   cin>>n;
   state=(1<<n)-1;
   for(int i=0;i<n;i++)
      {
         cin>>mm[i];    
         length[i]=mm[i].length();
      }
   memset(pre,0,sizeof(pre));
   
   for(int i=0;i<n;i++)
      {   
        for(int j=0;j<n;j++)
        {
           if(i==j)continue; 
           pre[i][j]=fn(mm[i],mm[j]);      
        } 
      } 
   for(int i=0;i<1050;i++)
      for(int j=0;j<11;j++)
           dp[i][j]=inf; 
   for(int i=0;i<n;i++)
   {
      dp[(1<<i)][i]=length[i];        
   }
   memset(vis,0,sizeof(vis));
   for(int i=0;i<state;i++)
   {
      for(int k=0;k<n;k++)
      {
         if(i&(1<<k)==0)
             continue;
         for(int j=0;j<n;j++)
         {   
             if(i&(1<<j))
                 continue;
             int newstate=((1<<j)|i);
             int tmp=dp[i][k]+length[j]-pre[k][j];
             if(dp[newstate][j]>tmp)
                  dp[newstate][j]=tmp;        
         }
                     
      }     
   }
   int msum=inf;
   for(int i=0;i<n;i++)
   {   
       msum=min(dp[state][i],msum);
   }
   cout<<msum<<endl; 
   
               
} 
return 0;} 



 

 

AC Program(注释版):
 
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm> 
#include<string.h>
#include<map>
#define inf 10000000
using namespace std;
int vis[1050];
string mm[12];
int pre[12][12];
int length[12];
int dp[1050][12];//求i状态下以k结尾的最短字符串长度.  
int fn(string s1 ,string s2)
{
   int len1=s1.length();
   int len2=s2.length();
     for(int i=len2;i>=1;i--)    //i表示截取几个数据    
     {
          if((len1-i)<0)continue;//注意了!! 
          string t1=s1.substr(len1-i);//从len1-i开始,默认剪刀最后 
          //system("pause");
          string t2=s2.substr(0,i);//直接截取来匹配,不能直接在母串中查找,因为有可能存在重复的子串 
          if(t1.find(t2,0)==0)//string::npos注意:这个位置 
            {
                return i; 
            }       
     }
     return 0;
}
int main()
{
//freopen("f_1.in","r",stdin);
//freopen("f_1.out","w",stdout); 
int test,n,state;
cin>>test;
while(test--)
{
   
   cin>>n;
   state=(1<<n)-1;
   for(int i=0;i<n;i++)
      {
         cin>>mm[i];    
         length[i]=mm[i].length();
      }
   //预处理.
   memset(pre,0,sizeof(pre));
   
   for(int i=0;i<n;i++)
      {   
        for(int j=0;j<n;j++)
        {
           if(i==j)continue; 
           pre[i][j]=fn(mm[i],mm[j]);      
        } 
      } 
   //初始化
   for(int i=0;i<1050;i++)
      for(int j=0;j<11;j++)
           dp[i][j]=inf; 
   for(int i=0;i<n;i++)
   {
      dp[(1<<i)][i]=length[i];        
   }
   memset(vis,0,sizeof(vis));
   for(int i=0;i<state;i++)
   {
      for(int k=0;k<n;k++)
      {
         //if(dp[i][k]==inf)
           //  continue; 
         if(i&(1<<k)==0)//k这个串不在里面就不用枚举以他为结尾的呗
             continue;
         for(int j=0;j<n;j++)
         {   
             //if(i==j)
               //  continue;
             if(i&(1<<j))//加入编号为j的字符串 
                 continue;
             int newstate=((1<<j)|i);
             int tmp=dp[i][k]+length[j]-pre[k][j];
             if(dp[newstate][j]>tmp)
                  dp[newstate][j]=tmp;        
         }
                     
      }     
   }
   int msum=inf;
   for(int i=0;i<n;i++)
   {

       //cout<<i<<" "<<dp[state][i]<<endl;     
       msum=min(dp[state][i],msum);
   }
   cout<<msum<<endl; 
                 
} 
//system("pause");
return 0;} 



 

 

 
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值