题目链接:http://poj.org/problem?id=2817
题意:有n个单词,如果两个单词相邻,可以在一个单词前面填充一些空格,使得两个单词对应位置上的字母相同(这些位置可以不相邻),最大的相同数量为这两个单词所产生的值。现在要找到一种排列方式,使得这些单词产生值之和最大。
思路:先计算出这些单词两两相邻所产生的值,在LCS的基础上做了一个小改动。然后用状态压缩DP计算,当前已经选了哪些单词作为状态,这些单词当中的最后一个选的单词是哪个。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod %100000007
const int maxn = 12;
int n;
int len[maxn];
char str[maxn][15];
int a[maxn][maxn];
int dp[1<<10][maxn];
int cal(int x,int y)
{
int f[maxn][maxn];
Clean(f,0);
int lenx = len[x];
int leny = len[y];
int ans = 0;
rep(i,0,lenx-1)
rep(j,0,leny-1)
{
if ( str[x][i] == str[y][j] )
{
f[i+1][j+1] = f[i][j] + 1;
}
else f[i+1][j+1] = f[i][j];
ans = max(ans,f[i+1][j+1]);
}
return ans;
}
bool init()
{
scanf("%d",&n);
if ( n < 1 ) return false;
getchar();
rep(i,1,n)
{
gets(str[i]);
len[i] = strlen(str[i]);
}
rep(i,1,n)
rep(j,1,n)
if ( i != j ) a[i][j] = cal(i,j);
Clean(dp,-1);
return true;
}
int main()
{
while(init())
{
int uplim = 1<<n;
rep(i,1,uplim-1)
{
rep(j,1,n)
if ( i & 1<<(j-1) )
{
int last = i & ~(1<<(j-1));
if ( last == 0 )
{
dp[i][j] = 0;
break;
}
rep(k,1,n)
if ( ( last & 1<<(k-1) ) && dp[last][k] != -1 )
dp[i][j] = max( dp[i][j] , dp[last][k] + a[k][j] );
}
}
int ans = 0;
rep(i,1,n) ans = max( ans , dp[uplim-1][i] );
cout<<ans<<endl;
}
return 0;
}