【状压dp+输出】 poj 1795 DNA Laboratory

版权声明:转载标注来源喔~ https://blog.csdn.net/iroy33/article/details/89963715

题意:给你n个字符串,要求找到一个最小的串,这n个字符串都是它的子串

思路:定义dp[j][s]代表以i为开头,状态为j的字符串最小长度,dp[j][s|(1<<j)]=min(dp[j][s|(1<<j)],dp[i][s]+cost[j][i])

cost[j][i]是把字符串j拼在字符串i前面使得总长度增加的长度,只需要考虑j的末尾和i的开头有多少相同

然后代码其实不难写,毕竟很暴力。但是我在计算cost的时候死了,因为我没有考虑公共长度为0。。。

关于输出:输出的时候我也死了,我记录了下一个是哪个字符串,自己写的样例都过了,呵呵哒嘤嘤嘤,明白原因了!我记录的是s[i]是最小的,但是输出的时候我只要输出s[idx].size()-cost[idx][a]开始的,这个不一定是最小的!

注意每个样例之后要多输出一行

参考博客

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int dp[18][1<<18];          //dp[i][j]以i为最前面一个,状态为j的最小长度
int next[18][1<<18];        //开头好求,已经能知道是谁作为开头最小了
int cost[18][18];           //将i接在j之前需要增加的长度
string s[18];
int n;
//int ans=INF;
string ans;
int idx=0;
void pre()
{
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j)
            if(s[i].find(s[j])!=string::npos)
                s[j]=s[i];
    sort(s,s+n);
    n=unique(s,s+n)-s;
    memset(cost,0,sizeof(cost));
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j)
        {
            if(i==j) continue;
            int len_i=s[i].size(),len_j=s[j].size();
            int len=min(len_i,len_j);
            for(int k=0;k<=len;++k)
            {
               // cout<<l<<' '<<s[i].substr(len_i-l,l)<<' '<<s[j].substr(0,l)<<endl;
                if(s[i].substr(len_i-k)==s[j].substr(0,k))
                    cost[i][j]=len_i-k;

            }
        }
}
void dfs(int id, int state)
{
	if(state == 0)   return;

	string tmp; int next_id = -1;
	for(int i = 0; i < n; i++) if(i != id && (state >> i & 1)) {    //不是开头并且state里面有
        if(dp[id][state] == dp[i][state & ~(1 << id)] + cost[id][i]) {
            string t = s[i].substr(s[id].length() - cost[id][i], s[i].length());
            if(next_id == -1 || t < tmp) {  //意思是找到的不一定是字典序最小的emm
                tmp = t;
                next_id = i;
            }
        }
	}
	ans += tmp;
	dfs(next_id, state & ~(1 << id));
}

void solve()
{
    memset(dp,INF,sizeof(dp));
    for(int i=0;i<n;++i)
    {
        dp[i][1<<i]=s[i].size();
        next[i][1<<i]=-1;
    }
    int tot=(1<<n)-1;
    for(int s=0;s<=tot;++s)
    {
        for(int i=0;i<n;++i)
        {
            if(dp[i][s]!=INF)               //i最前面
            {
                for(int j=0;j<n;++j)
                {
                    if(i!=j&&!(s&(1<<j)))
                    {
                        if(dp[j][s|(1<<j)]>dp[i][s]+cost[j][i]) //由于i按字典序排序了,所以是>
                        {
                           dp[j][s|(1<<j)]=dp[i][s]+cost[j][i];
                           next[j][s|(1<<j)]=i;                 //为什么这种操作不行!啊,因为我i和j重复部分是没有输出的!
                        }
                    }

                }
            }


        }
    }
    int sum=INF;
    idx=0;
    for(int i=0;i<n;++i)
    {
        //c//out<<dp[i][1<<(n-1)]<<endl;   dp[0][1<<(n-1)]没更新上,并且dp[1][xxx]没有加上。。
        if(sum>dp[i][(1<<n)-1])
        {
            sum=dp[i][(1<<n)-1];
            idx=i;
        }

    }
    ans=s[idx];
//    cout<<ans<<endl;
//    cout<<s[idx]<<endl;
}
//这个不对。。。┭┮﹏┭┮
//void output(int idx,int state)
//{
//    cout<<s[idx];
//    int a=next[idx][state];
//    while(a!=-1)
//    {
//        for(int i=s[idx].size()-cost[idx][a];i<s[a].size();++i)
//            cout<<s[a][i];
//        state=state&~(1<<idx);
//        idx=a;
//        a=next[a][state];
//    }
//    cout<<endl;
//}
int main()
{
    int t,kase=0;
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=0;i<n;++i)
            cin>>s[i];
        pre();
//        for(int i=0;i<n;++i)
//        {
//            for(int j=0;j<n;++j)
//                cout<<cost[i][j]<<' ';
//            cout<<endl;
//        }


        solve();
        cout<<"Scenario #"<<++kase<<":"<<endl;
       // output(idx,(1<<n)-1);
        dfs(idx,(1<<n)-1);
        cout<<ans<<endl;
        cout<<endl;




    }
}

猜你喜欢

转载自blog.csdn.net/iroy33/article/details/89963715