题意
寻找哈密顿图。路径上的每个点都有权值,对于每条边,权值为v[i]*v[j]。如果i和j相连,j和k相连,并且i和k之间有桥,则权值再加上v[i]*v[j]*v[k]。问权值最大的路径权值是多少?有多少种组成该权值路径的方案,一个方案的正序和逆序视为一个方案。
题解
自己处理的时候考虑的不严密,做了很多麻烦的东西。比如说对于是否存在通路的判断,自己考虑的时候选用了并查集去进行处理,但实际上这是完全没有必要的,只需要在dp数组赋初始值的时候谨慎一点(注意过滤掉无通路的情况)就可以了。
还有就是对于上一个节点的记录,自己在处理的时候专门开了一个数组进行记录,搞得很麻烦。看了Kuangbin大佬的题解才意识到,dp数组多开一维,就可以很方便的记录上一个节点。ORZ。
另外的话,关于路径的统计,一直没有找到好的办法,因为正序和逆序路径判断很难搞,我自己记录了一下起始点和终止点,但这样并不能很好的去重。最后依然是参考了Kuangbin大佬的题解,在DP数组状态转移的时候搞一个num数组,用来专门记录可以转移到这个状态的路径数目。最后由于统计了正向和逆向的,正好是去重后的二倍,因此/2就可以了。
代码
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 8200
#define EPS 1e-10
#define MOD 100000000
using namespace std;
int dp[15][15][MAXN];
int num[15][15][MAXN];
bool can[15][15];
int value[15];
int main() {
int kase;
scanf("%d",&kase);
W(kase--) {
memset(can,false,sizeof(can));
memset(dp,-1,sizeof(dp));
memset(num,0,sizeof(num));
int n,m;
scanf("%d%d",&n,&m);
UP(i,0,n) {
scanf("%d",&value[i]);
}
if(n==1) {
printf("%d 1\n",value[0]);
continue;
}
UP(i,0,m) {
int a,b;
scanf("%d%d",&a,&b);
a--,b--;
can[a][b]=can[b][a]=true;
}
UP(i,0,n) {
UP(j,0,n) {
if(i==j||!can[i][j])
continue;
dp[i][j][(1<<i)|(1<<j)]=value[i]*value[j];
num[i][j][(1<<i)|(1<<j)]=1;
}
}
UP(s,0,1<<n) {
UP(i,0,n) {
if(!(s&(1<<i)))
continue;
UP(j,0,n) {
if(i==j||!(s&(1<<j))||!can[i][j])
continue;
UP(k,0,n) {
if(i==k||j==k||!(s&(1<<k))||!can[j][k]||dp[j][k][s-(1<<i)]==-1)
continue;
int add=value[i]*value[j];
if(can[i][k]) {
add+=value[k]*value[i]*value[j];
}
if((dp[j][k][s-(1<<i)]+add)>dp[i][j][s]) {
// cout<<j<<" "<<k<<" "<<s-(1<<i)<<" "<<add<<" "<<dp[j][k][s-(1<<i)]<<endl;
dp[i][j][s]=dp[j][k][s-(1<<i)]+add;
num[i][j][s]=num[j][k][s-(1<<i)];
} else if((dp[j][k][s-(1<<i)]+add)==dp[i][j][s]) {
num[i][j][s]+=num[j][k][s-(1<<i)];
}
}
}
}
}
int maxsum=0;
LL maxnum=0;
UP(i,0,n) {
UP(j,0,n) {
if(i==j||!can[i][j])
continue;
if(dp[i][j][(1<<n)-1]>maxsum) {
maxsum=dp[i][j][(1<<n)-1];
maxnum=num[i][j][(1<<n)-1];
} else if(dp[i][j][(1<<n)-1]==maxsum) {
maxnum+=num[i][j][(1<<n)-1];
}
}
}
if(maxsum!=0) {
UP(i,0,n) {
maxsum+=value[i];
}
}
printf("%d %I64d\n",maxsum,maxnum/2);
}
}