poj2288IslandsandBridges_状态压缩dp_哈密尔顿回路问题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
poj2288IslandsandBridges_状态压缩dp_哈密尔顿回路问题
题⽬描述:哈密尔顿路问题。
n个点,每⼀个点有权值,设哈密尔顿路为 ,Ci的权值为Vi,⼀条哈密尔顿路的值分为三部分计算:
1.每⼀个点的权值之和
2.对于图中的每⼀条CiCi+1,加上Vi*Vi+1
3.对于路径中的连续三个点:CiCi+1Ci+2,若在图中,三点构成三⾓形,则要加上Vi*Vi+1*Vi+2
求⼀条汉密尔顿路可以获得的最⼤值,并且还要输出有多少条这样的哈密尔顿路。
这道题的状态感觉不是很难想,因为根据⼀般的哈密尔顿路问题,⾸先想到的是设计⼆维状态,dp[i , s]表⽰当前在i点,⾛过的点形成状态集合s。
但是这道题在求解值的时候有⼀个不⼀样的地⽅,就是第三部分,如果还是设计成⼆维的状态,就会很⿇烦,因为每加⼊⼀个新点,要判断新点、当前点、倒数第⼆个点是否构成三⾓形,所以要记录倒数第⼆个点。
很⾃然地想到扩展状态的维数,增加⼀维,记录倒数第⼆个点。
1> 设计状态:
dp[i , j , s]表⽰当前站在j点,前⼀个点是i点,形成的状态集合是s,此时的最⼤值,way[i , j , s]记录当前状态下达到最⼤值的路径数;
2> 状态转移:
设k点不在集合s中,且存在边<j , k>
设q为下步到达k点获得的最⼤值
令r = s + (1<<k),为当前站在点k,前⼀个点为j,形成状态集合r
若i,j,k形成三⾓形,则q = dp[i][j][s] + v[k] + v[j]*v[k] + v[i]*v[j]*v[k]
否则,q = dp[i][j][s] + v[k] + v[j]*v[k];
若q⼤于dp[j][k][r];则:
dp[j][k][r] = q
way[j][k][r] = way[i][j][s];
若q等于dp[j][k][r],则:
way[j][k][r] += way[i][j][s];
3> 初始化:
显然,若i点到j点有边,则:
dp[i][j][(1<<i)+(1<<j)] = v[i] + v[j] + v[i]*v[j];
way[i][j][(1<<i)+(1<<j)] = 1;
4> 结果的产⽣:
最后的结果我们要枚举点i和j,找到最⼤的dp[i][j][(1<<n)-1],并且更新记录路径数ansp,最后ansp要除2才是结果,因为题⽬最后⼀句话,正向反向是⼀样的路。
此外,需要注意的是discuss提到的特殊情况,要⽤__int64,并且注意n等于1时,最⼤值就是第⼀个点的权值,路径数为1。
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN =13;
const int MAXS =1<<MAXN|1;
#define ll __int64
ll dp[MAXN][MAXN][MAXS],way[MAXN][MAXN][MAXS];
int map[MAXN][MAXN],v[MAXN];
int n,m,s;
void stateDp(){
int i,j,p,k;
memset(dp,-1,sizeof(dp));
memset(way,0,sizeof(way));
for(i=0;i<n;i++)
for(j=0;j<n;j++){
if(map[i][j]){
dp[i][j][(1<<i)+(1<<j)]=v[i]+v[j]+v[i]*v[j];
way[i][j][(1<<i)+(1<<j)]=1;
}
}
for(p=3;p<s;p++){
for(i=0;i<n;i++){
if(!(p&1<<i))//如果该状态第i城市没有路过就跳过
continue;
for(j=0;j<n;j++){
if(i==j||!(p&1<<j)||dp[i][j][p]==-1)
continue;
for(k=0;k<n;k++){
if(p&1<<k||!map[j][k])//如果k存在该状态则跳过
continue;
int r=p+(1<<k);//状态加⼊k城市
ll q=dp[i][j][p]+v[k]+v[j]*v[k];//更新价值
if(map[i][k]){//当构成环时更新价值
q+=v[i]*v[j]*v[k];
}
if(q>dp[j][k][r]){
dp[j][k][r]=q;
way[j][k][r]=way[i][j][p];
}else if(q==dp[j][k][r]){//相等时,有多个相等价值路径 way[j][k][r]+=way[i][j][p];
}
}
}
}
}
}
int main(int argc, char** argv) {
int t,x,y,i,j;
scanf("%d",&t);
while(t--){
memset(map,0,sizeof(map));
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
scanf("%d",&v[i]);
for(i=0;i<m;i++){
scanf("%d%d",&x,&y);
map[x-1][y-1]=map[y-1][x-1]=1;
}
s=1<<n;
if(n==1){
printf("%d %d\n",v[0],1);
continue;
}
stateDp();
ll ansv=-1,ansp=0;
for(i=0;i<n;i++)
for(j=0;j<n;j++){
if(i==j)continue;
if(dp[i][j][s-1]>ansv){//s-1为经过所有岛
ansv=dp[i][j][s-1];
ansp=way[i][j][s-1];
}else if(dp[i][j][s-1]==ansv){
ansp+=way[i][j][s-1];
}
}
printf("%I64d %I64d\n",ansv==-1?0:ansv,ansp/2); }
return 0;
}。