// ShellDawn
// POJ2288
// No.21
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define MM(x) memset(x,0,sizeof(x))
#define LL long long
using namespace std;
#define maxn 15
#define maxm (1<<13)
int n;
LL V[maxn];
LL dp[maxm][maxn][maxn];
bool E[maxn][maxn];
LL way[maxm][maxn][maxn];
int main(){
int T;
scanf("%d",&T);
while(T--){
int m;
scanf("%d%d",&n,&m);
// 城市编号从0开始,方便状态
for(int i=0;i<n;i++){
scanf("%I64d",&V[i]);
}
MM(E);
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
E[a-1][b-1] = E[b-1][a-1] = true;
}
if(n==1){
printf("%I64d 1\n",V[0]);
continue;
}
MM(dp);MM(way);
// 找两个岛,初始化状态
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(!E[i][j] || i==j) continue;
int s = (1<<i) | (1<<j) ;
dp[s][i][j] = dp[s][j][i] = V[i] * V[j] + V[i] + V[j];
//printf("(%d %d %I64d)\n",i,j,dp[s][i][j]);
way[s][i][j] = way[s][j][i] = 1;
}
}
// 状态转移,dp三维度的三重枚举
for(int s = 3 ; s<(1<<n);s++){
for(int i=0;i<n;i++){
// 用i去找新点
if( ! (s & (1<<i)) ) continue;
for(int j=0;j<n;j++){
// 不能选相同点
if(i==j) continue;
// 若状态不合法
if(dp[s][i][j] == 0) continue;
// 点j不在状态中
if(!(s & (1<<j)) ) continue;
// 找扩展点
for(int k=0;k<n;k++){
if(s & (1<<k) || !E[i][k] ) continue;
int news = s|(1<<k);
LL t = dp[s][i][j] + V[i]*V[k] + V[k];
// 存在三角
if(E[k][j]) t += V[i]*V[j]*V[k];
//
if(dp[news][k][i] == t){
way[news][k][i] += way[s][i][j];
}
else if(dp[news][k][i] < t){
dp[news][k][i] = t;
way[news][k][i] = way[s][i][j];
}
}
}
}
}
int s = (1<<n) - 1;
LL ans = 0;
LL maxway = 0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
// 不是回路
if(!E[i][j]) continue;
//printf("<%d %d %d>\n",i,j,way[s][i][j]);
if(dp[s][i][j] > ans){
ans = dp[s][i][j];
maxway = way[s][i][j];
}
else if(dp[s][i][j] == ans){
maxway += way[s][i][j];
}
}
}
printf("%I64d %I64d\n",ans,maxway/2);
}
return 0;
}
No.21 -POJ2288 状压dp求汉密顿回路
最新推荐文章于 2019-08-01 17:17:09 发布