简单搜索&&进阶搜索 - Virtual Judge (vjudge.net)
【题目描述】
现在我们给定了数个DNA序列,请你构造出一个最短的DNA序列,使得所有我们给定的DNA序列都是它的子序列。 例如,给定"ACGT", "ATGC", "CGTT", "CAGT", 你可以构造的一个最短序列为"ACAGTGCT",但是需要注意的是,这并不是此问题的唯一解。
【输入】
第一行含有一个数t,代表数据组数。 每组数据的第一行是一个数n,代表给定的DNA序列数量;接下来的n行每行一个字符串s,代表给定的n个DNA序列。 1<=n<=8,1<=|s|<=5
【输出】
对于每一组数据,输出一行中含有一个数,代表满足条件的最短序列的长度。
解题思路
这个题要求最短的序列,那么我们肯定要从一个小的长度开始往大的长度遍历,而且对于 DNA 字符串有很多种情况,每个字符的位置可以放四种‘A’,'C','T','G',而对于这么多种情况,可能不能直接用 dfs 一条路走到底,再走第二条,第三条……最后找到最短的序列是多少。
所以要想办法简化搜索,如果明明可以判断这条路一直走肯定会超过最大的值,就不继续进行搜索了。
对于简化搜索,如果判断的时间复杂度还很高,也很不划算,所以只要粗略地估计,其中用 getst()函数来实现,返回找到的还需要增加的步数。
x 是已经放置的字母数,maxt 是传入的希望的最小字母数(如果当前的 maxt 不满足要求,就自增,一步一步找它的最小值)其中(getst()+x)>maxt 时就不要往这个方向搜索了(这是最理想的情况,因为希望的是:有几个字符串中的字母少了,但是只要添加一个字母,所有的字符串的字母都增加一个)。
代码如下:
#include<iostream>
#include<string>
#include<stdio.h>
using namespace std;
char a[10][10];
char dna[5] = { 'A','C','T','G' };
int book[10];//匹配到的位置
int len[10];
int n, maxt;
int maxd(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int getst()
{
int m = 0;
//大致计算出当前拼接的字符串还需要拼接多少个字母
//用一层循环大概计算,其中(gets()+maxt<=实际拼接次数)
//最理想的情况是:有字符串的字母少了,但是只要添加一个字母,所有的字符串的字母都增加一个
for (int i = 0; i < n; i++)
{
m = maxd(m, len[i] - book[i]);
}
return m;
}
int check(int x)
{
int i, j, w;
w = getst();
if (x + w > maxt)
return 0;
if (w == 0)
return 1;
for (i = 0; i < 4; i++)
{
int k[10], flag=0;
memcpy(k, book, sizeof(book));//记录book数组,否则后面变化后找不到了
for (j = 0; j < n; j++)
{
if (dna[i] == a[j][book[j]])
{
flag = 1;
book[j]++;
}
}
if (flag == 1)
{
if (check(x + 1))
return 1;
//回溯,如果进入该层循环后,不符合,就将先前记录的k数组的值放入book数组
memcpy(book, k, sizeof(k));
}
}
return 0;
}
int IDA()
{
while (check(0)==0)
maxt++;
return 1;
}
int main()
{
int t, i, j;
scanf("%d", &t);
while (t--)
{
memset(book, 0, sizeof(book));
memset(len, 0, sizeof(len));
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%s", a[i]);
len[i] = strlen(a[i]);
}
maxt = 0;
if(IDA())
printf("%d\n",maxt);
}
}