您的位置:首页 > 其它

poj2288 状压dp

2017-08-08 10:16 393 查看
传送门

题意:有n个点,m条边,每个点有一个权值,求一条哈密顿回路,但是权值计算不同,包括三部分:1,经过的所有点的权值相加。2,经过的连续两个点的权值的乘积。3,能够构成三角型的连续三个点的乘积。这些全部加起来就是这条回路的总权值。输出最大权值和这个最大权值的路线有多少条。

状压dp,由于当前位置只和前两个点的位置有关,所以我们用dp[s][i][j],表示从i走到了j,目前倒数第二走到的是i,倒数第一走到的是j,目前走过的岛的状态集合是s(其二进制表示状态,0<=s<=(1<<13)-1,最多13个岛)的权值,ways[s][i][j]表示路径的条数。

首先把已知的边的权值算出来,

for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
{
if(Map[i][j])
{
dp[(1<<i)|(1<<j)][i][j]=val[i]+val[j]+val[i]*val[j];
ways[(1<<i)|(1<<j)][i][j]=1;
}

}


很好理解,第一个地点走i岛,第二个地点走j岛,状态就是用’或’表示,权值就是val[i]+val[j]+val[i]*val[j],满足两个相连的点嘛,加上自身值再加上乘积。

然后我们就可以枚举了,我们倒数第一个点在j,倒数第二个点在i,那么如果有一条路径j->k,并且k还没走过呢(状态s里没有第k个岛,即s的二进制第k位为0),我们就从j走到k,下面分两种情况;

1,k与i点不可直达,
dp[s|(1<<k)][j][k]=dp[s][i][j]+val[k]+val[k]*val[j]


2,k可以直达i,

dp[s|(1<<k)][j][k]=dp[s][i]j] + val[k] + val[j] * val[k] + val[i] * val[j] * val[k];


因为我们要求最大值,所以赋值之前比较一下,比之前大才更新,并同时更新ways[s|k][j][k],如果相等的话,我们就只更新ways,
ways[s|(1<<k)][j][k]+=ways[s][i][j];


最后遍历一遍所有状态s为“n个1”(
s=(1<<n)-1
)的dp[s][i][j]值,选择最大的即可,

int s=(1<<n)-1;
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
if(Map[i][j])
{
if(dp[s][i][j]>ans1)
{
ans1=dp[s][i][j];
ans2=ways[s][i][j];
}
else if(dp[s][i][j]==ans1)
ans2+=ways[s][i][j];
}
}
}


ans1更新权值,ans2更新路径条数

AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=13;
LL dp[(1<<N)]

,ways[(1<<N)]

;
LL val
;
int Map

;
void solve(int n)
{
memset(dp,-1,sizeof(dp));
memset(ways,0,sizeof(ways));
for(int i=0; i<n; i++) for(int j=0; j<n; j++) { if(Map[i][j]) { dp[(1<<i)|(1<<j)][i][j]=val[i]+val[j]+val[i]*val[j]; ways[(1<<i)|(1<<j)][i][j]=1; } }

for(int s=0; s<(1<<n); s++)
{
for(int i=0; i<n; i++)
if(s&(1<<i))///s包含第i个岛
{
for(int j=0; j<n; j++)
if(s&(1<<j)&&Map[i][j]&&dp[s][i][j]!=-1)///s包含第j个岛,且dp不为-1,且i-j有路径
{
for(int k=0; k<n; k++) ///寻找j到k的路径
if(Map[j][k]&&!(s&(1<<k)))///s不包含k岛,但是j到k有路径,所以我们要走一走
{
LL temp = dp[s][i][j]+val[k]+val[k]*val[j];///单纯的走到k的权值保存一下
if(Map[k][i])///k能到i的话形成三角
{
temp+=val[i]*val[j]*val[k];
}
if(dp[s|(1<<k)][j][k]<temp)
{
dp[s|(1<<k)][j][k]=temp;
ways[s|(1<<k)][j][k]=ways[s][i][j];
}
else if(dp[s|(1<<k)][j][k]==temp)
{
ways[s|(1<<k)][j][k]+=ways[s][i][j];
}
}
}
}
}
}
int main()
{
int T,n,m;
int u,v;
cin>>T;
while(T--)
{
cin>>n>>m;
memset(Map,0,sizeof(Map));
for(int i=0; i<n; i++)
cin>>val[i];

for(int i=0; i<m; i++)
{
cin>>u>>v;
u--;
v--;
Map[u][v]=Map[v][u]=1;
}
if(n==1)
{
printf("%lld 1\n",val[0]);
continue;
}
solve(n);
LL ans1,ans2;
ans1=ans2=0;
int s=(1<<n)-1; for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { if(Map[i][j]) { if(dp[s][i][j]>ans1) { ans1=dp[s][i][j]; ans2=ways[s][i][j]; } else if(dp[s][i][j]==ans1) ans2+=ways[s][i][j]; } } }
cout<<ans1<<" "<<ans2/2<<endl;
}

return 0;
}
/**

2
3 3
2 2 2
1 2
2 3
3 1
4 6
1 2 3 4
1 2
1 3
1 4
2 3
2 4
3 4

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