题目链接:http://poj.org/problem?id=2288
题目大意:给你N个岛和M座桥,接下来N个数表示从1-N每座岛的价值,在接下来M 条路u,v,表示u,v之间有一座桥,问能否经过每一个点一次,如果能,求出最大值和方案数,如果两个小岛a,b相连,则经过他们的价值为 v[a]+v[b]+v[a]*v[b];如果三座小岛可以组成三角形,则他们的价值为再加上v[a]*v[b]*v[c];
解题思路:定义dp[s][i][j]为当前状态为s,且当前处于i,之前处于j的最大价值,则dp[s][i][j]=max(dp[s][i][j],dp[s-(1<<i)][j][k]+value);其中value 为他们之间的价值,需要特殊判定。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define LL long long
LL dp[1<<14][14][14],sum[1<<14][14][14],v[14],link[14][14];
int n,m;
void init()
{
memset(dp,-1,sizeof(dp));
memset(sum,0,sizeof(sum));
memset(v,0,sizeof(v));
memset(link,0,sizeof(link));
}
void solve()
{
int num=(1<<n);
for(int s=0; s<num; s++)
for(int i=0; i<n; i++)
{
if( !(s&(1<<i)) ) continue;
for(int j=0; j<n; j++)
{
if( !(s&(1<<j)) || (i==j) || link[i][j]==0) continue;
for(int k=0; k<n; k++)
{
if( !(s&(1<<k)) || (k==j) || (k==i) || link[j][k]==0) continue;
int news=s-(1<<i); //i不在状态内,j,k在状态内
if(dp[news][j][k]==-1) continue;
LL temp=dp[news][j][k]+v[i]+v[j]*v[i];
if(link[i][k])
temp+=v[i]*v[j]*v[k];
if(temp>dp[s][i][j])
{
dp[s][i][j]=temp;
sum[s][i][j]=sum[news][j][k];
}
else if(temp==dp[s][i][j])
sum[s][i][j]+=sum[news][j][k];
}
}
}
LL ans1=-1,ans2=0;
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
{
if(i==j) continue;
if(dp[num-1][i][j]>ans1)
{
ans1=dp[num-1][i][j];
ans2=sum[num-1][i][j];
}
else if(dp[num-1][i][j]==ans1)
ans2+=sum[num-1][i][j];
}
if(ans1==-1)
printf("0 0\n");
else
printf("%I64d %I64d\n",ans1,ans2/2);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%I64d",&v[i]);
int a,b;
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
a--,b--;
link[a][b]=link[b][a]=1;
int s=((1<<a)|(1<<b));
dp[s][a][b]=dp[s][b][a]=v[a]+v[b]+v[a]*v[b];
sum[s][a][b]=sum[s][b][a]=1;
}
if(n==1) {printf("%I64d 1\n",v[0]);continue;}
solve();
}
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
*
22 3
69 1
*/