题目链接:http://poj.org/problem?id=1699
题意:给出一系列的DNA序列,按照一定排列,使相邻的序列之间重复的进行合并,问得到最短的序列的长度。
每个串只使用一次,两串之间有一定的距离,使这个距离最长的环游序列可得最长路的TSP问题,结果就是总长度减去路径长度。小规模的TSP问题可用状态压缩DP解决。
dp[state][i]为state状态下,以i为最后一个点的最长路线,则
dp[state|1<<j][j] = max( dp[state|1<<j][j] , dp[state][i]+map[i][j]);
Code:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> #define M 24 char s[16][M]; int l[16]; int map[16][16]; int n; int nxt[M],ext[M]; int tot,res,stk[M]; int v[M]; int ExtendKmp(char s[],int ls,char t[],int lt) { int i,j,k; int Len,L; j=0; while(t[j+1]==t[j]&&j+1<lt) j++; nxt[1]=j,k=1; for(i=2;i<lt;i++){ Len=k+nxt[k],L=nxt[i-k]; if(Len>L+i) nxt[i]=L; else{ j=Len-i>0?Len-i:0; while(t[i+j]==t[j]&&i+j<lt) j++; nxt[i]=j,k=i; } } j=0; while(s[j]==t[j]&&j<lt&&j<ls) j++; ext[0]=j,k=0; for(i=1;i<ls;i++){ Len=k+ext[k],L=nxt[i-k]; if(Len>L+i) ext[i]=L; else{ j=Len-i>0?Len-i:0; while(s[i+j]==t[j]&&i+j<ls&&j<lt) j++; ext[i]=j,k=i; } } for(i=0;i<ls;i++){ if(i+ext[i]==ls) break; } return ls-i; } void UpAns() { int i,r=0; for(i=1;i<n;i++) r+=map[stk[i-1]][stk[i]]; if(tot-r<res) res=tot-r; } void Dfs(int k) { int i; if(k==n){ UpAns(); return ; } for(i=0;i<n;i++){ if(!v[i]){ v[i]=1; stk[k]=i; Dfs(k+1); v[i]=0; } } } int main() { int t; int i,j; scanf("%d",&t); while(t--){ scanf("%d",&n); for(tot=i=0;i<n;i++){ scanf("%s",s[i]); l[i]=strlen(s[i]); tot+=l[i]; } for(i=0;i<n;i++){ for(j=0;j<n;j++){ if(i!=j) map[i][j]=ExtendKmp(s[i],l[i],s[j],l[j]); } }res=tot; memset(v,0,sizeof(v)); Dfs(0); printf("%d\n",res); } return 0; }