状压DP。
f[i][j][k]表示走的点的状态是i,倒数第二个岛是j,最后一个是k的mincost,cnt表示该状态下的方案数。统计方案数就和23号的考试题一个套路。
注意加个特判,判1个点的状态。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,m,f[1<<14][14][14],g[14][14],val[14],T;
long long cnt[1<<14][14][14];
int main() {
scanf("%d",&T);
while(T--) {
memset(f,-1,sizeof f);
memset(g,0,sizeof g);
memset(cnt,0,sizeof cnt);
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&val[i]);
for(int i=1,u,v;i<=m;i++) {
scanf("%d%d",&u,&v);u--,v--;
g[u][v]=g[v][u]=1;f[(1<<u)|(1<<v)][u][v]=f[(1<<u)|(1<<v)][v][u]=val[u]+val[v]+val[u]*val[v],cnt[(1<<u)|(1<<v)][u][v]=cnt[(1<<u)|(1<<v)][v][u]=1;
}
if(n==1){printf("%d 1
",val[0]);continue;}
for(int i=0;i<1<<n;i++) {
for(int j=0;j<n;j++)
for(int k=0;k<n;k++)
if(g[j][k]&&(i&(1<<j))&&~f[i][j][k])
for(int l=0;l<n;l++) {
if(g[k][l]&&!(i&(1<<l))) {
int tmp=f[i][j][k]+val[l]+val[k]*val[l];
if(g[j][l]) tmp+=val[j]*val[k]*val[l];
if(f[i|(1<<l)][k][l]<tmp) f[i|(1<<l)][k][l]=tmp,cnt[i|(1<<l)][k][l]=cnt[i][j][k];
else if(f[i|(1<<l)][k][l]==tmp) cnt[i|(1<<l)][k][l]+=cnt[i][j][k];
}
}
}
long long ans1=0,ans2=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(g[i][j]){
if(ans1<f[(1<<n)-1][i][j]) ans1=f[(1<<n)-1][i][j],ans2=cnt[(1<<n)-1][i][j];
else if(ans1==f[(1<<n)-1][i][j]) ans2+=cnt[(1<<n)-1][i][j];
}
printf("%lld %lld
",ans1,ans2/2);
}
}