您的位置:首页 > 其它

状态压缩DP题目小节(二)

2013-04-04 11:26 381 查看
最近做的状态压缩DP小节:

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4257

zoj 4257

一堆气体相互碰撞产生能量,求最后能产生的最大能量,应该是很基础的状态压缩DP吧,设dp[flag]表示状态flag时能产生的最大能量,(flag中1表示该气体还存在,0表示该气体已经消失)边界条件是flag所有位都为一时,这时产生的能量为0,然后就枚举最后剩下的气体,求最大值即可。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
int dp[1<<10];
int bo[11][11];
int n;
int max(int a,int b)
{
return a>b?a:b;
}
int dfs(int flag)
{
if(dp[flag]!=-1)
return dp[flag];
int i,j;
int ans=0;
for(i=1;i<=n;i++)
{
if(flag&1<<(i-1))
{
for(j=1;j<=n;j++)
{
if(j!=i&&(flag&1<<(j-1))==0)
{
ans=max(ans,dfs(flag^(1<<(j-1)))+bo[i][j]);
}
}
}
}
return dp[flag]=ans;
}
int main()
{
//freopen("dd.txt","r",stdin);
while(scanf("%d",&n)&&n)
{
int i,j;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
scanf("%d",&bo[i][j]);
}
memset(dp,-1,sizeof(dp));
int ans=0;
dp[(1<<n)-1]=0;
for(i=0;i<n;i++)
{
ans=max(ans,dfs(1<<i));
}
printf("%d\n",ans);
}
return 0;
}


http://acm.hdu.edu.cn/showproblem.php?pid=3001

hdu 3001:

类似于TSP问题,但是这里不同的是每个点最多可以走两次,而不是一次,所以我们可以用三进制来表示每一个状态,我们设dp[now][flag]表示目前在now点状态为flag时的最小花费,则边界条件为当(1<<(now-1))==flag时,这时表示起点在now点,还没开始走,则此时的最小花费是0,还有一个边界条件为(1<<(now-1))*2==flag,这表示从now点走到now点,这是不可能的,所以把最小花费设为无穷大,剩下的就是状态转移了。

dp[now][flag]=dp[pre][flag']+map[pre][now],满足以下几个条件:

1:pre!=now且map[pre][now]不为-1(也就是存在边)

2:flag的第pre位不为0

3:flag'的第now位比flag的第now位少1.

我们求最小值即可,我们要枚举所有每一位都不为0的状态,取它们的最小值即为答案。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define inf 2100000000
using namespace std;
int bo[11][11];
int dp[11][60000];
int bit[60000][11];
int pow[12];
void init()
{
int i,j;
for(i=0;i<=59048;i++)
{
int t=i;
for(j=1;j<=10;j++)
{
bit[i][j]=t%3;
t/=3;
}
}
pow[1]=1;
for(i=2;i<=11;i++)
pow[i]=pow[i-1]*3;
}
int check(int x)
{
while(x)
{
if(x%3==0)
return 0;
x/=3;
}
return 1;
}
int n,m;
int min(int a,int b)
{
return a<b?a:b;
}
int dfs(int now,int flag)
{
if(dp[now][flag]!=-1)
return dp[now][flag];
if(pow[now]==flag)
return dp[now][flag]=0;
if(pow[now]*2==flag)
return dp[now][flag]=inf;
int ans=inf,i;
for(i=1;i<=n;i++)
{
if(i!=now&&bit[flag][i]&&bo[i][now]!=-1)
{
int tmp=dfs(i,flag-pow[now]);
if(tmp!=inf)
{
ans=min(ans,tmp+bo[i][now]);
}
}
}
return dp[now][flag]=ans;
}
int main()
{
// freopen("dd.txt","r",stdin);
init();
while(scanf("%d%d",&n,&m)!=EOF)
{
int i,a,b,c;
memset(bo,-1,sizeof(bo));
memset(dp,-1,sizeof(dp));
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(bo[a][b]==-1||bo[a][b]>c)
bo[a][b]=bo[b][a]=c;
}
int ma=pow[n+1]-1,mi=ma/2;
int ans=inf;
for(i=1;i<=n;i++)
{
for(int j=mi;j<=ma;j++)
{
if(check(j))
ans=min(ans,dfs(i,j));
}
}
if(ans==inf)
ans=-1;
printf("%d\n",ans);
}
return 0;
}


http://poj.org/problem?id=3311

poj 3311

也是类似于TSP问题,不过这个时候每个点可以走无限次,所以我们要用依次floyd算法求一下个点之间的最短路,这里设dist[u][v]表示u和v之间的最短路,然后就和平常求TSP问题一样了,我们还是设dp[now][flag]表示当前在now点状态为flag的最小花费,边界条件为flag==(1<<(now-1))时,此时表示从起点开始走第一次到达到达now,为其他店还没开hi走,则dp[now][flag]=dist[0][now],其他情况基本和上体一样。

注意题目要求最后一定要回到0点。

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define inf 2100000000
using namespace std;
int map[11][11];
int dp[11][1<<10];
int n;
int min(int a,int b)
{
return a<b?a:b;
}
int dfs(int now,int flag)
{
//printf("%d %d\n",now, flag);
if(dp[now][flag]!=-1)
return dp[now][flag];
int i,j;
if(flag==(1<<(now-1)))
return dp[now][flag]=map[0][now];
int ans=inf;
if(now==0)
{
for(i=1;i<=n;i++)
{
ans=min(ans,dfs(i,flag)+map[i][now]);
}
}
else
{
for(i=1;i<=n;i++)
{
if(i!=now&&flag&(1<<(i-1)))
{
ans=min(ans,map[i][now]+dfs(i,flag^(1<<(now-1))));
}
}
}
return dp[now][flag]=ans;
}
void floyd(int n)
{
int i,j,k;
for(i=0;i<=n;i++)
{
for(j=0;j<=n;j++)
{
for(k=0;k<=n;k++)
{
map[j][k]=min(map[j][k],map[j][i]+map[i][k]);
}
}
}
}
int main()
{
//freopen("dd.txt","r",stdin);
int i,j;
while(scanf("%d",&n)&&n)
{
for(i=0;i<=n;i++)
{
for(j=0;j<=n;j++)
scanf("%d",&map[i][j]);
}
floyd(n);
memset(dp,-1,sizeof(dp));
dp[0][0]=0;
printf("%d\n",dfs(0,(1<<n)-1));
}
return 0;
}


poj 2288

还是类似于TSP问题,不过这里对于花费做了新的定义,花费分为三部分

1:路径中每个点的权值之和。

2:路径中相邻两个点的权值之积的和。

3:若存在路径pi->pi+1->pi+2,且pi和pi+2之间有边,则花费还要加上这三点的权值之和。

我们要求花费最大的路径以及这样的路径的个数

所以我们社状态时不能只考虑当前点,还要考虑之前的点了,所以我们设dp[now][pre][flag]表示当前点在now,上一个点在pre,此时状态为flag时的最大花费。设way[now][flag][flag]为dp[now][pre][flag]取最大值时的路径条数。则接下来我们就和求TSP问题时差不多了,只是求花费时稍微麻烦一点,边界条件是当now为起点时,即(1<<(now-1))==flag时,此时花费为now点的权值,此时的路径条数为1.代码还有一些细节,具体实现请参考代码。

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 100010
#define ll long long
using namespace std;
ll dp[14][14][1<<13];
ll way[14][14][1<<13];
int bo[14][14];
int v[14];
void init()
{
memset(bo,0,sizeof(bo));
memset(way,0,sizeof(way));
memset(dp,-1,sizeof(dp));
}
ll max(ll a,ll b)
{
return a>b?a:b;
}
int n;
ll dfs(int now,int pre,int flag)
{
// printf("f");
if(dp[now][pre][flag]!=-1)
return dp[now][pre][flag];
if(1<<(now-1)==flag)
{
way[now][pre][flag]=1;
return dp[now][pre][flag]=v[now];
}
int i,tru=0;
ll ans=0;
for(i=1;i<=n;i++)
{
if(i!=now&&i!=pre&&(flag&(1<<(i-1))))
{
tru=1;
if(bo[i][pre])
{
ll tmp=0;
tmp=dfs(pre,i,flag^(1<<(now-1)));
if(tmp)
{
tmp+=v[now]+v[now]*v[pre];
if(bo[i][now])
tmp+=v[now]*v[pre]*v[i];
ans=max(ans,tmp);
}
}
}
}
if(!tru)
{
dp[now][pre][flag]=dfs(pre,0,flag^(1<<(now-1)))+v[now]+v[now]*v[pre];
way[now][pre][flag]=1;
return dp[now][pre][flag];
}
if(ans)
{
for(i=1;i<=n;i++)
{
if(i!=now&&i!=pre&&bo[i][pre]&&(flag&(1<<(i-1))))
{
ll tmp=0;
tmp=dfs(pre,i,flag^(1<<(now-1)));
if(tmp)
{
tmp+=v[now]+v[now]*v[pre];
if(bo[i][now])
tmp+=v[now]*v[pre]*v[i];
if(ans==tmp)
{
way[now][pre][flag]+=way[pre][i][flag^(1<<(now-1))];
}
}
}
}
}
return dp[now][pre][flag]=ans;
}
int main()
{
//freopen("dd.txt","r",stdin);
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
int m,i,a,b,j;
init();
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&v[i]);
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
bo[a][b]=bo[b][a]=1;
}
ll ans=0,num=0;
if(n==1)
printf("%d 1\n",v[1]);
else
{
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(i!=j&&bo[i][j])
{
dfs(i,j,(1<<n)-1);
ans=max(ans,dp[i][j][(1<<n)-1]);
}
}
}
if(ans==0)
printf("0 0\n");
else
{
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(i!=j&&bo[i][j]&&dp[i][j][(1<<n)-1]==ans)
{
num+=way[i][j][(1<<n)-1];
}
}
}
printf("%I64d %I64d\n",ans,num/2);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: