UVA - 1204 Fun Game (状压DP)

题意:

一群小孩(至少两个)围成一圈做游戏。每一轮从某个小孩开始往他左边或者右边传手帕。一个小孩拿到手帕后在手帕上写上自己的性别,男孩写B,女孩写G,然后按照相同的方向传递给下一个小孩,每一轮都可能在任何一个小孩写完后停止,现在游戏已经进行了n轮,已知n轮中每轮手帕上留下的字,问最少可能有几个小孩。

分析:

定义状态dp[i][j][x]表示当前拿的字符串状态为i,最后一次拿的字符串为j,j的正反状态为x的最少长度。

        预处理去掉所有的被其他串包含的串,然后处理出所有串的重叠长度。

        状态的转移就很好写了嘛,考虑当前一个没有拿到的串,假设为j,正反状态为y(j要求没有拿过,即i&(1<<j)为0),当前拿到的串的集合为s且当前最后一次的串为i,i的状态是x

        dp[s|(1<<j)][j][y]=dp[s][i][x]+length[j]-overlap[i][j][x][y]

        length[j]代表第j个字符串的长度,overlap[i][j][x][y]代表正反状态为x的第i字符串和正反状态为y的第j字符串的重叠长度(i左j右)
 

#include<bits/stdc++.h>

using namespace std;

const int maxn=1<<17;

int dp[maxn+10][20][2];         //dp[i][j][x]表示当前拿的字符串状态为i(状态压缩的集合),最后一次拿的字符串为j,j的正反状态为x的最少长度。
int n;                          //
int over[20][20][2][2];         //overlap[i][j][x][y]代表正反状态为x的第i字符串和正反状态为y的第j字符串的重叠长度(i左j右)
int len[20];                    //length[j]代表第j个字符串的长度
string arr[20][2];

struct Node{
    string str,rstr;
    bool operator < (const Node & t) const {
        return str.size()<t.str.size();
    }
};

inline void update(int &x,int v){
    if(x<0||x>v) x=v;
}

void solve(){
    memset(dp,-1,sizeof dp);
    int limi=(1<<n)-1;

    dp[1][0][0]=len[0];
    for(int s=1;s<limi;++s)
    	for(int i=0;i<n;++i)
    		for(int x=0;x<2;++x)
    			if(dp[s][i][x]>=0)
    				for(int k=1;k<n;++k)
    					if(!(s&1<<k))
    						for(int y=0;y<2;++y)
        						update(dp[s|(1<<k)][k][y],dp[s][i][x]+len[k]-over[i][k][x][y]);

    int ans=-1;
    for(int i=0;i<n;++i)
    	for(int x=0;x<2;++x)
    		if(dp[limi][i][x]>=0)
       			update(ans,dp[limi][i][x]-over[i][0][x][0]);

    if(ans<=1) ans=2;
    cout<<ans<<endl;
}

int overlap(const string& a,const string& b){
    int n1=a.size(),n2=b.size();
    bool flag=true;
    for(int i=1;i<n1;++i,flag=true)
        if(n2+i>n1){
            for(int j=0;j+i<n1;++j)
                if(a[i+j]!=b[j]){
                    flag=false;
                    break;
                }
            if(flag)
                return n1-i;
        }
    return 0;
}

void init(){
    int n2=0;
    Node node[20];
    for(int i=0;i<n;i++){
        cin>>node[i].str,node[i].rstr=node[i].str;
        reverse(node[i].rstr.begin(),node[i].rstr.end());
    }
    sort(node,node+n);
    for(int i=0,j;i<n;i++){
        for(j=i+1;j<n;j++){
            if(node[j].str.find(node[i].str)!=string::npos||node[j].str.find(node[i].rstr)!=string::npos)
                break;
        }
        if(j==n){
            arr[n2][0]=node[i].str;
            arr[n2][1]=node[i].rstr;
            len[n2++]=node[i].str.size();
        }
    }
    n=n2;
    for(int i=0;i<n;++i)
    	for(int j=0;j<n;++j)
    		for(int k=0;k<2;++k)
    			for(int l=0;l<2;++l)
        			over[i][j][k][l]=overlap(arr[i][k],arr[j][l]);
}

int main(){
    while(cin>>n,n){
        init();
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40679299/article/details/84140710