传送门
一道难在初始化的题目.
状态表示应该不难想到,状态i里某一位为1表示该点已经打通了,为0表示没打通.dp[i][k][l]表示在状态i的时候最后一个连通的点是k,倒数第二个连通的点是l能得到的最大值.转移方程也不难.枚举所有的1的位置j,然后根据题目给的价值转移,要注意的是j k l 三者都连通的时候需要计算第三个代价.
状态表示和转移都很好解决,问题在于初始化.在只打通一个点和两个点这种情况下是不能这样转移的.所以对于这两种状态直接暴力的去做一遍.预处理出来.具体实现可以看代码中的count函数.
这题还有计数的要求,而且顺着和逆着算同一种方案,解决方法也很简单,把总方案数除2就好了.
再提一点在能够正确的对状态初始化的情况下尽量把初值设置为不可到达的状态(比如无穷小或者无穷大),这样能确保程序不出一些奇奇怪怪的问题.
代码
#pragma GCC optimize(2)
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#include <bits/stdc++.h>
using namespace std;
const int N = 14;
inline void read(int &a){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){
x=x*10+ch-'0';ch=getchar();}
a = x*f;
}
LL dp[1<<14][N][N],w[N],ct[1<<14][N][N];
int f[N][N];
int count(int s){
int res = 0;
while(s){
res += (s%2);
s>>=1;
}
return res;
}
int main(){
int t;
cin >> t;
while(t--){
int n,m;
cin >> n >> m;
fir(i,0,(1<<n)-1) fir(j,0,n-1) fir(k,0,n-1) dp[i][j][k] = -1e15;
mem(ct,0);
mem(f,0);
mem(w,0);
fir(i,0,n-1) cin >> w[i];
fir(i,1,m){
int x,y;
cin >> x >> y;
x--;y--;
if(x == y) continue;
f[x][y] = f[y][x] = 1;
}
LL ans = 0;
fir(i,0,n-1){
dp[1 << i][i][n] = w[i];
ct[1 << i][i][n] = 1;
}
fir(i,0,(1<<n)-1){
if(count(i) == 1) continue;
fir(j,0,n-1){
int b1 = i>>j&1;
if(!b1) continue;
fir(k,0,n-1){
int b2 = i>>k&1;
if(!b2) continue;
if(!f[j][k]) continue;
LL cur = w[j] + w[j]*w[k];
if(count(i) == 2){
LL newval = cur + dp[i^(1 << j)][k][n];
if(dp[i][j][k] < newval){
ct[i][j][k] = ct[i^(1 << j)][k][n];
dp[i][j][k] = newval;
}
else if(dp[i][j][k] == newval){
ct[i][j][k] += ct[i^(1 << j)][k][n];
}
continue;
}
fir(l,0,n-1){
int b3 = i>>l&1;
if(!b3) continue;
if(!f[k][l] || l == k || l == j) continue;
LL tmp = 0;
if(f[l][j]) tmp += w[j]*w[k]*w[l];
LL newval = tmp + cur + dp[i^(1 << j)][k][l];
if(dp[i][j][k] < newval){
ct[i][j][k] = ct[i^(1 << j)][k][l];
dp[i][j][k] = newval;
}
else if(dp[i][j][k] == newval){
ct[i][j][k] += ct[i^(1 << j)][k][l];
}
}
}
}
}
LL cnt = 0;
fir(i,0,n-1){
fir(j,0,n-1){
if(i == j) continue;
ans = max(ans,dp[(1<<n)-1][i][j]);
}
}
fir(i,0,n-1){
fir(j,0,n-1){
if(i == j) continue;
if(ans == dp[(1<<n)-1][i][j]){
cnt += ct[(1<<n)-1][i][j];
}
}
}
cout << ans << " " << cnt/2 << endl;
}
return 0;
}
/*
dp[i][j][k] 表示目前状态是i,终点是j,终点的前一个是k的max
*/