4126:DNA
内存限制:
1024kB
描述
考虑一段DNA单链,上面有N个基因片段。这里的基因片段可重叠(例如AGCTC包含AGC和CTC),不可倒置(例如AGCTC不包含TCG)。要问这样的单链最短长度是多少。
输入
输入的第一行是一个正整数T(不超过13),表示数据组数。每组数据若干行,其中第一行一个正整数N(不超过9),表示基因片段的数目,接下来N行每行一个基因片段,由AGCT四个字母组成,且长度介于1和15之间(含两端)。
输出
每组数据输出一样,表示最短的单链长度包含这N个基因片段。
样例输入
1 5 TCGG GCAG CCGC GATC ATCG
样例输出
11
本道题可以用状压dp解决
状态i表示当前已经拼好的字符串,如0101,表示使用了第0个和第2个字符串。
dp[i][j]表示以j为结尾的,拼好状态为i的字符串的最短长度。
状压dp有两种更新思路,一种为向后更新,不断寻找可以用当前状态来更新的下一个状态;一种为向前更新,不断寻找使用之前的状态来更新现在的状态。
在这道题中,使用向后更新更好理解。
考虑一种用例:
1
3
TAGCGC
GCG
CAA
output:8
因为我们的dp设计的是以j为“结尾”的,所以,怎么来更新dp[3][0]呢?(注:3==011)
考虑当dp[i][j]时,如果genes[k]完全包含于gens[j],那么我们可以用dp[i][j]来更新dp[i|(1<<k)][j],此时结尾的字符不变
否则,我们可以用dp[i][j]来更新dp[i|(1<<k)][k],此时结尾字符变为gengs[k]。
如果向前更新的话,对每一个dp[i][j],我要寻找dp[i-(1<<j)][k]来更新,但是如果是完全包含的情况,
就不好考虑进去,没有考虑进去,就会wa,就像注释中的更新方法。
附上代码:
#include <iostream>
#include<string.h>
#include<stdio.h>
#include<string>
#include<vector>
#include<algorithm>
#include<queue>
#include<math.h>
#include<map>
using namespace std;
const int inf = 1e9 + 7;
int dp[1 << 10][20];
int len[15];
string genes[15];
int n;
int common[15][15];
int get_common_len(int l1, int l2)
{
int ans = 0;
int i = 0;
while (i < len[l1])
{
int k = i;
int j = 0;
bool flag = true;
while (k < len[l1] && j < len[l2])
{
if (genes[l1][k] != genes[l2][j])
{
flag = false;
break;
}
k++;
j++;
}
if (flag)
{
ans = j;
break;
}
i++;
}
return ans;
}
int main()
{
int T;
cin >> T;
while (T--)
{
cin >> n;
for (int i = 0; i < (1 << n); i++)
for (int j = 0; j < n; j++)
dp[i][j] = inf;
for (int i = 0; i < n; i++)
{
cin >> genes[i];
len[i] = genes[i].length();
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i == j)
{
//cout << "0 ";
common[i][j] = 0;
continue;
}
common[i][j] = get_common_len(i, j);
//cout << common[i][j] << " ";
}
//cout << endl;
}
for (int i = 0; i < n; i++)
dp[1 << i][i] = len[i];
for (int i = 1; i < (1 << n); i++)
for (int j = 0; j < n; j++)
{
if (i&(1 << j))//i状态含有j
{
for (int k = 0; k < n; k++)
{
if (k == j) continue;
if (i&(1 << k)) continue;//i状态不含有k,用状态dp[i][j]更新状态dp[i|(1<<k)][k]
if (common[j][k] == len[k])
dp[i | (1 << k)][j] = min(dp[i | (1 << k)][j], dp[i][j]);
else
dp[i | (1 << k)][k] = min(dp[i | (1 << k)][k], dp[i][j] + len[k] - common[j][k]);
}
}
}
//for (int i = 1;i<(1<<n);i++)
//for (int j = 0; j < n; j++)
//{
// if (i&(1 << j))//i状态中含有j,更新状态dp[i][j]
// {
// if (i == (1 << j)) dp[i][j] = len[j];
// else
// {
// for (int k = 0; k < n; k++)
// {
// if (k == j) continue;
// if (!(i&(1 << k))) continue;
// //i状态中含有k
// dp[i][j] = min(dp[i - (1 << j)][k] + len[j] - common[k][j], dp[i][j]);
// }
// }
// }
//}
int ans = inf;
for (int i = 0; i < n; i++)
ans = min(ans, dp[(1 << n) - 1][i]);
cout << ans << endl;
}
return 0;
}