题意 : n个点,m条边,每个点有一个权值。求一条哈密顿路径,使这条哈密顿路径的权值最大。哈密顿路径的权值由三个部分组成:
1. 哈密顿路径上每个点权值的和
2. 哈密顿路径上每两个相邻的点乘积的和
3. 哈密顿路径上三个点可以构成三角形,把这三个点权值的乘积加上
问权值最大的哈密顿路径权值是多少 ,有几条。
数据范围,n不超过13,所以状压dp
两个数组 dp[status][i][j] 记录权值,表示状态为status时,上上个经过的点是 i , 上个经过的点是 j 的权值。
num[status][i][j] 表示路径的条数。
然后就是枚举状态,判断合法之后进行状态转移即可。
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
int dp[(1<<13)][13][13];
ll mp[(1<<13)][13][13];
int num[13];
int vis[13][13];
int n,m;
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>num[i];
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++)
{
int u,v;
cin>>u>>v;
u--,v--;
vis[u][v]=vis[v][u]=1;
}
if(n==1)
{
cout<<num[0]<<" 1"<<endl;
continue;
}
memset(dp,-1,sizeof(dp));
memset(mp,0,sizeof(mp));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(vis[i][j]&&i!=j)
{
dp[(1<<i)|(1<<j)][i][j]=num[i]+num[j]+num[i]*num[j];
mp[(1<<i)|(1<<j)][i][j]=1;
}
}
}
int tol=(1<<n);
for(int i=0;i<tol;i++)
{
for(int j=0;j<n;j++)
{
if((i&(1<<j))!=0)
{
for(int k=0;k<n;k++)
{
if(vis[j][k]&&j!=k&&(i&(1<<k))!=0&&dp[i][j][k]!=-1)
{
for(int l=0;l<n;l++)
{
if((i&(1<<l))==0&&vis[k][l]&&l!=j&&l!=k)
{
int t=dp[i][j][k]+num[l]+num[k]*num[l];
if(vis[j][l])
t+=num[j]*num[k]*num[l];
if(dp[(i|(1<<l))][k][l]<t)
{
dp[(i|(1<<l))][k][l]=t;
mp[(i|(1<<l))][k][l]=mp[i][j][k];
}
else if(dp[(i|(1<<l))][k][l]==t)
{
mp[(i|(1<<l))][k][l]+=mp[i][j][k];
}
}
}
}
}
}
}
}
int ans=0;
long long res=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(i!=j&&vis[i][j])
{
if(ans<dp[(tol-1)][i][j])
{
ans=dp[(tol-1)][i][j];
res=mp[(tol-1)][i][j];
}
else if(ans==dp[(tol-1)][i][j])
{
res+=mp[(tol-1)][i][j];
}
}
}
}
cout<<ans<<' '<<res/2<<endl;
}
return 0;
}