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 }
View Code

解题思路

 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了很久……惨……

posted @ 2018-06-03 11:04  TobicYAL  阅读(1237)  评论(0编辑  收藏  举报