您的位置:首页 > 其它

Gym 101343.J - Husam and the Broken Present 2(状压dp)

2017-10-24 12:19 441 查看
题意:给你n个(n≤15)序列连续片段,每个片段包括m个数字(m≤100)。最后问你构造一个数字序列,使得这n个串都是它的子串,这个序列的最短长度为多少。

思路:显然可以2^n枚举当前使用了哪些片段,然后显然这些串连成一串,那么需要再多一个维度来记录结尾的是哪个片段。dp[i][j]的方程雏形就有了。其次考虑到,方程的转移 需要记录两个段之间,至少添加几位,那么就是添加串的长度-两串前后缀公共部分。所以需要预处理。但是注意一个地方:完全包含的时候需要特判处理。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 5;
const int INF = 0x3f3f3f3f;
int a[20][maxn];
int cover[maxn][maxn];//i能否覆盖j
int linkLen[maxn][maxn];//j接在i后面,两者最长公共部分
int dp[(1<<16) + 5][20];
int illegal[20];
bool checkCover(int i, int j, int st)
{
for(int k = 1; k <= a[j][0]; k++)
{
if(a[i][st + k - 1] != a[j][k]) return false;
}
return true;
}
int checkLink(int i, int j, int st)
{
int ret = 0;
for(int k = 1; k <= a[j][0] && st + k - 1 <= a[i][0]; k++)
{
if(a[i][st + k - 1] == a[j][k]) ret++;
else return 0;
}
return ret;
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i][0]);
for(int j = 1; j <= a[i][0]; j++)   scanf("%d", &a[i][j]);
}
//先检查是否包含。
memset(cover, 0, sizeof(cover));
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
if(illegal[i])  continue;
if(i == j)  continue;
int leni = a[i][0], lenj = a[j][0];
if(leni < lenj)   continue;
for(int st = 1; st + lenj - 1 <= leni; st ++)
{
if(checkCover(i, j, st))
{
cover[i][j] = 1;
illegal[j] = 1;
break;
}
}
}
}
int cnt = 0;
for(int i = 0; i < n; i++)
{
if(illegal[i])  continue;
for(int j = 0; j <= a[i][0]; j++)   a[cnt][j] = a[i][j];
cnt++;
}
n = cnt;
//    cout << " n = " << n << endl;
//最长公共部分
memset(linkLen, 0, sizeof(linkLen));
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
if(i == j)  continue;
int len = 0;
for(int st = 1; st <= a[i][0]; st++)
{
len = max(len, checkLink(i, j, st));
}
linkLen[i][j] = len;
//            printf("linkLen[%d][%d] = %d\n", i, j, linkLen[i][j]);
}
}
memset(dp, INF, sizeof(dp));
for(int i = 0; i < n; i++)  dp[1 << i][i] = a[i][0];
for(int S = 0; S < (1 << n); S++)
{
for(int i = 0; i < n; i++)
{
if(S & (1 << i))
{
for(int j = 0; j < n; j++)
{
if(i == j)  continue;
if(S & (1 << j))    continue;
dp[S | (1 << j)][j] = min(dp[S][i] + a[j][0] - linkLen[i][j], dp[S | (1 << j)][j]);
}
}
}
}
int ans = INF;
for(int i = 0; i < n; i++)  ans = min(ans, dp[(1 << n) - 1][i]);
printf("%d\n", ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: