题意:给出n个岛和它们之间联通的m条路径,给出权值的计算方法,求权值最大哈密顿回路的权值和数量。
n<=13.
权值的计算方法为:
1.经过的所有点的权值之和;
2.经过的连续的两个点的权值的乘积;
3.能够组成三角形的三个点的权值的乘积。
根据岛是否走过最多有(1<<13)种状态。
dp[s][i][j] = max{ dp[s][i][j] , dp[s’][j][k] + tmp };
tmp = val[i]*val[j] + val[i];
if(i,j,k能够组成三角形) tmp += (val[i]*val[j]*val[k]);
对于路径的数量,如果更新dp[s][i][j],则更新num[s][i][j] = num[s’][j][k];
如果不更新dp[s][i][j],但是 dp[s][i][j] == dp[s’][j][k] + tmp,
则num[s][i][j] += num[s’][j][k].
路径数量最后要除以2,因为反向的路径是一样的,不能重复计算。
s表示当前的状态,i表示当前所在的岛,j表示上一步走的岛,k是所有岛中任意符合要求的岛。
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int n = 13;
const int maxn = 1<<13;
int edge[n][n];
int dp[maxn][n][n];
int val[n];
long long num[maxn][n][n];
int N,M;
int main()
{
int T;
cin>>T;
while(T--){
cin>>N>>M;
for(int i = 0;i < N;i++) cin>>val[i];
int x,y;
memset(edge,0,sizeof(edge));
for(int i = 0;i < M;i++){
cin>>x>>y;
edge[x-1][y-1] = edge[y-1][x-1] = 1;
}
if(N == 1) {cout<<val[0]<<" "<<"1"<<endl;continue;}
memset(dp,-1,sizeof(dp));
memset(num,0,sizeof(num));
for(int i = 0;i < N;i++)
for(int j = 0;j < N;j++){
if(i != j&&edge[i][j]){//初始化dp和num,
//将走过任意一条边能够到达的状态的dp和num初始化。
dp[(1<<i)|(1<<j)][i][j] = val[i] + val[j] + val[i] * val[j];
num[(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)) == 0) continue;
for(int j = 0;j < N;j++){
if(((s&(1<<j))==0)||i == j||!edge[i][j]) continue;
for(int k = 0;k < N;k++){
if(((s&(1<<k))==0)||i == k||j == k) continue;
int newS = s-(1<<i);
if(dp[newS][j][k] == -1) continue;//若为-1,
//则表示不存在路径从第k座岛到达第j座岛
int tmp = val[i]*val[j]+val[i];//求出tmp值
if(edge[i][k]) //i,k之间有边,表示能够组成三角形,更新tmp
tmp += val[i] * val[j] * val[k];
if(dp[s][i][j] < dp[newS][j][k] + tmp){
dp[s][i][j] = dp[newS][j][k] + tmp;
num[s][i][j] = num[newS][j][k];
}
else if(dp[s][i][j] == (dp[newS][j][k] + tmp)){
num[s][i][j] += num[newS][j][k];
}
}
}
}
}
long long ans = -1,Num = 0;
int p = (1<<N) - 1;
for(int i = 0;i < N;i++){
for(int j = 0;j < N;j++){
if(i == j||!edge[i][j]) continue;
if(dp[p][i][j] > ans){
ans = dp[p][i][j];
Num = num[p][i][j];
}
else if(dp[p][i][j] == ans){
Num += num[p][i][j];
}
}
}
if(ans == -1) cout<<"0"<<" "<<"0"<<endl;
else cout<<ans<<" "<<Num/2<<endl;
}
return 0;
}