北大POJ百练 4126 DNA

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< 否则,我们可以用dp[i][j]来更新dp[i|(1< 如果向前更新的话,对每一个dp[i][j],我要寻找dp[i-(1< 就不好考虑进去,没有考虑进去,就会wa,就像注释中的更新方法。

附上代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
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<                     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<         //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;
}

你可能感兴趣的:(ACM,dp)