题目大意:
- 给出一个带点权的无向图,在图上找哈密顿路,使其价值最大,并统计价值最大的哈密顿路的条数.同一条路正走和反走看作同一种方案.
- 路径的价值分为三部分:
(1).路径上所有点的点权和.
(2).路径上所有边的价值和.对于路径C上相邻的两点Ci和Ci+1,它们之间的路径价值为v[Ci]*v[Ci+1].
(3)对于路径上的连续三点Ci-1,Ci,Ci+1,如果Ci-1与Ci+1之间有边,那么可以另有附加价值v[Ci-1]*v[Ci]*v[Ci+1].
分析:
TSP问题的变形。
因为关系到连续三点的价值,在TSP问题的原有状态表示上加一维。
dp[S][i][j]:状态为S(一个二进制数,第k位上的1表示点k已走过,0表示点k未走过) , 最后经过的两个点为i,j。
dp[S][j][k]=max{dp[S][j][k],dp[S’][i][j]+w[k]+w[k]*w[j]+(i,j,k组成三角形)?w[i]*w[j]*w[k] : 0
初始状态:dp[0][i][j]=w[i]+w[j]+w[i]*w[j]
目标状态:max{dp[(1<< n )-1][a][b]}
方案数在dp的时候顺便算出来就好了。
注意:
1.是哈密顿路,不是哈密顿回路
2.由于“同一条路正走和反走看作同一种方案.”,最后path要除以2
3.算方案数要用long long
#include<cstdio>
#include<cstring>
#define MAXN 13
int n,m,mat[MAXN+10][MAXN+10],dp[(1<<MAXN)+10][MAXN+10][MAXN+10],S,w[MAXN+10];
long long way[(1<<MAXN)+10][MAXN+10][MAXN+10];
void read()
{
int x,y;
scanf("%d%d",&n,&m);
memset(mat,0,sizeof mat);
for(int i=0;i<n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
if(x==y)
continue;
x--,y--;
mat[x][y]=mat[y][x]=true;
}
S=(1<<n)-1;
}
void DP()
{
memset(dp,-1,sizeof dp);
memset(way,0,sizeof way);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(mat[i][j]){
dp[(1<<i)|(1<<j)][i][j]=w[i]+w[j]+w[i]*w[j];
way[(1<<i)|(1<<j)][i][j]=1;
}
for(int s=0;s<=S;s++)
for(int i=0;i<n;i++){
if(!(s&(1<<i))) continue;
for(int j=0;j<n;j++){
if(!mat[i][j]||!(s&(1<<j))||dp[s][i][j]==-1)
continue;
for(int k=0;k<n;k++){
if((s&(1<<k))||!mat[j][k]) continue;
int t=dp[s][i][j]+w[k]+w[k]*w[j]+((mat[i][k])?w[i]*w[j]*w[k]:0);
if(dp[s|(1<<k)][j][k]<t)
dp[s|(1<<k)][j][k]=t,way[s|(1<<k)][j][k]=way[s][i][j];
else if(dp[s|(1<<k)][j][k]==t)
way[s|(1<<k)][j][k]+=way[s][i][j];
}
}
}
int ans=0;
long long path=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(mat[i][j]){
if(ans<dp[S][i][j])
ans=dp[S][i][j],path=way[S][i][j];
else if(ans==dp[S][i][j])
path+=way[S][i][j];
}
printf("%d %I64d\n",ans,path/2);
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
read();
if(n==1){
printf("%d 1\n",w[0]);
continue;
}
DP();
}
}