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;
}
说说这道题的想法,状态压缩。我发现以前在写状态压缩的总结的一条规律(数据上限小可以考虑状态压缩)并没有什么用。。。。首先要知道是一个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;
}
相关文章推荐
- 100道动态规划——24 UVA 1633 Dyslexic Gollum 状态压缩DP 挺好的题 因为窝没想到嘛
- 100道动态规划——32 UVA 12170 Easy Climb 通过分析减少状态数 单调队列
- 100道动态规划——27 POJ 1185 炮兵阵地 状态压缩,预处理,滚动数组
- uva 11795 Mega Man's Mission(动态规划-状态压缩DP)
- 100道动态规划——19 UVA 12105 Bigger is Better 状态的定义以及转移方程
- 100道动态规划——8 UVA 1631 Locker 递推,状态的定义以及状态转移方程
- 100道动态规划——26 UVA 12099 The Bookcase 状态的定义,递推,背包
- 100道动态规划——34 UVA 10559 Blocks 状态的定义 状态转移方程
- UVa 11795 Mega Man's Mission(动态规划-状态压缩DP)
- 100道动态规划——31 POJ 2411 && POJ 2663 && POJ 3420 状态压缩 矩阵快速幂
- 100道动态规划——5 UVA 10118 Free Candies 记忆化搜索 以及 证明状态
- uva 11795 Mega Man's Mission(动态规划-状态压缩DP)
- 动态规划状态压缩题解
- 状态压缩动态规划 -- 炮兵阵地
- Pieces(hdu4628,状态压缩的动态规划)
- 动态规划之状态压缩
- 动态规划专题:树上DP和状态压缩DP
- POJ3254 Corn Fields 状态压缩动态规划,状压动规
- 插头与轮廓线与基于连通性状态压缩的动态规划
- 【树型状态压缩动态规划】【NOI2006】网络收费