18.06.03 POJ 4126:DNA 15年期末05(状压DP)
描述
考虑一段DNA单链,上面有N个基因片段。这里的基因片段可重叠(例如AGCTC包含AGC和CTC),不可倒置(例如AGCTC不包含TCG)。要问这样的单链最短长度是多少。
输入
输入的第一行是一个正整数T(不超过13),表示数据组数。每组数据若干行,其中第一行一个正整数N(不超过9),表示基因片段的数目,接下来N行每行一个基因片段,由AGCT四个字母组成,且长度介于1和15之间(含两端)。输出每组数据输出一样,表示最短的单链长度包含这N个基因片段。
样例输入
1
5
TCGG
GCAG
CCGC
GATC
ATCG
样例输出
11
1 #include <iostream> 2 #include <string.h> 3 #include <algorithm> 4 #include <cstdio> 5 #include <stdlib.h> 6 #include <string> 7 #include <memory> 8 #include <queue> 9 10 using namespace std; 11 12 const int maxn = 9; 13 int f[1 << (maxn + 1)][maxn];//x为二进制表示的一维visited状态 y为当前字符串的最后一个基因片段 14 char genes[maxn + 1][20]; 15 bool invalid[maxn + 1]; 16 int l[maxn + 1]; 17 int chong[maxn + 1][maxn + 1]; 18 int n, minlen, base; 19 20 int dp(int state, int last) { 21 if (f[state][last]) 22 return f[state][last]; 23 f[state][last] = 1 << 10; 24 for (int i = 1; i <= n; i++) { 25 int flag = state & (1 << i); 26 if (invalid[i] && flag != 0) { 27 f[state][last] = min(dp(state - (1 << i), i) + l[last] - chong[last][i], f[state][last]); 28 } 29 } 30 return f[state][last]; 31 } 32 33 int main() 34 { 35 int t; 36 scanf("%d", &t); 37 while (t--) { 38 scanf("%d", &n); 39 for (int i = 1; i <= n; i++) 40 cin >> genes[i]; 41 memset(f, 0, sizeof(int)*(1 << (maxn+1))*maxn); 42 minlen = 2 << 10; 43 base = 1 << (n + 1); 44 for (int i = 1; i <= n; i++) { 45 invalid[i] = true; 46 f[0][i] = l[i] = strlen(genes[i]); 47 } 48 for (int i = 1; i <= n; i++) 49 for (int j = 1; j <= n; j++) 50 { 51 if (i != j && invalid[i] && invalid[j] && strstr(genes[i], genes[j]) != NULL) 52 { 53 invalid[j] = false; 54 base -= 1 << j; 55 } 56 if (i != j && invalid[i] && invalid[j]) { 57 int l0 = min(l[i], l[j]); 58 while (l0) { 59 if (strstr(genes[j], genes[i] + l[i] - l0) == genes[j]) 60 break; 61 l0--; 62 } 63 chong[i][j] = l0; 64 } 65 } 66 for (int i = 1; i <= n; i++) 67 if (invalid[i]) 68 minlen = min(dp(base - (1 << i) - 2, i), minlen); 69 printf("%d\n", minlen); 70 } 71 return 0; 72 }
解题思路:
f[x][y] 表示在x状态下,单链尾的基因片段是 genes[y] 时的最小长度
x状态的表示方式:主要思想是二进制,我这里设的是如果遍历过这个基因片段i,表示状态的二进制数中的第i位(从0算起)就变为0(这就是所谓的状态压缩吧)
然后主要是要把所有包含关系的基因片段剔除,网上搜到一个学长的代码其实是不对的,但他提供了一个很好的思路……不过也能A,说明数据还是比较弱的……
WA点:因为我只使用x状态数中的1~n位,在写的时候就要注意把第0位(也就是1)去掉, f[x][y] 的x范围也要开到 1<<10 ,事实证明是有9个基因片段的数据的。
然后后来因为忘改memset中的大小wa了很久……惨……
注定失败的战争,也要拼尽全力去打赢它;
就算输,也要输得足够漂亮。