您的位置:首页 > 其它

100道动态规划——25 UVA 1204 Fun Game 状态压缩 圆圈的处理 (100道完成了1/4啦)

2017-01-23 20:40 501 查看
        首先说一说,100道完成了1/4啦,虽然是值得稍稍庆祝一下的事情.......可是,紫书上的DP快写完了。。。以后去白书上刷题,然后白书刷完了我也不知道去哪儿刷了。。。

        说说这道题的想法,状态压缩。我发现以前在写状态压缩的总结的一条规律(数据上限小可以考虑状态压缩)并没有什么用。。。。首先要知道是一个DP呀,然后就算知道了是状态压缩也想不出来=_=

        我觉得紫书上说的非常的好,先处理一个简化版的问题,就是考虑某个字符串str使得这n个字符串皆为str的连续子串且str长度最短,然后可以扩展到考虑正反,加一维描述正反即可。至于原问题是圆圈,老实说这个我可能想不到,就是在做出答案之后减去最后一次的字符串和第一个字符串的重叠

        定义状态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 <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxm=1<<17;

inline void update(int& x,int v){if(x<0||x>v)x=v;}
void init(),slove();
int overlap(const string& a,const string& b);
int dp[maxm+10][20][2],n,over[20][20][2][2],len[20];
string arr[20][2];

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

int main(){
ios_base::sync_with_stdio(false);
while(cin>>n&&n){
init();
slove();
}
return 0;
}

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]);
}

void slove(){
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;
}



        
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息