题目链接

汉密尔顿图+状压dp

用二进制数表示路径,压缩状态 dp[stat][last][last_one] 表示当前状态的权值 ways[stat][last][last_one] 表示当前状态的个数

这里要注意的是如果 dp更新,ways要丢掉之前记录的个数,重新计数,否则累加。

代码有4重for循环,记得剪枝

dp用int 代替long long能快很多

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

using namespace std;

typedef long long ll;

const int Maxn = 13;
const int INF = 0x3f3f3f3f;
const long long LINF = 1e18;

bool G[Maxn+5][Maxn+5];
ll ways[(1<<Maxn)+5][Maxn+5][Maxn+5];
int dp[(1<<Maxn)+5][Maxn+5][Maxn+5], cost[Maxn+5];

void solve(int n) {
	int maxn = -1;
	for(int i = 3; i < (1<<n)-1; ++i) {
		for(int j = 1; j <= n; ++j) {
            if(!((i>>(j-1))&1)) continue;
			for(int k = 1; k <= n; ++k) {
			    if(!((i>>(k-1))&1) || !G[j][k]) continue;
				for(int v = 1; v <= n; ++v) {
					if(!G[j][v] || dp[i][j][k] == -1) continue;
					if((i>>(v-1))&1) continue;

					int stat = (i|(1<<(v-1)));
					if(G[v][j] && G[j][k] && G[v][k]) {
                        if(dp[stat][v][j] < dp[i][j][k]+cost[v]*cost[j]*(1+cost[k])) {
                            ways[stat][v][j] = ways[i][j][k];
                        } else if (dp[stat][v][j] == dp[i][j][k]+cost[v]*cost[j]*(1+cost[k])) {
                            ways[stat][v][j] += ways[i][j][k];
                        }
                        dp[stat][v][j] = max(dp[stat][v][j], dp[i][j][k]+cost[v]*cost[j]*(1+cost[k]));
					} else {
                         if(dp[stat][v][j] < dp[i][j][k]+cost[v]*cost[j]) {
                            ways[stat][v][j] = ways[i][j][k];
                        } else if(dp[stat][v][j] == dp[i][j][k]+cost[v]*cost[j]) {
                            ways[stat][v][j] += ways[i][j][k];
                        }
                        dp[stat][v][j] = max(dp[stat][v][j], dp[i][j][k]+cost[v]*cost[j]);
					}
				}
			}
		}
	}

	ll sum = 0, cnt = 0;
	for(int i = 1; i <= n; ++i) sum += cost[i];

	for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            if(maxn < dp[(1<<n)-1][i][j]) {
                maxn = dp[(1<<n)-1][i][j];
                cnt = ways[(1<<n)-1][i][j];
            } else if(maxn == dp[(1<<n)-1][i][j]) {
                cnt += ways[(1<<n)-1][i][j];
            }
        }
	}

	if(maxn == -1) printf("0 0\n");
	else printf("%lld %lld\n", maxn+sum, cnt/2);
}

int main(void)
{
	int T;
	scanf("%d", &T);
	while(T--) {
		int n, m, u, v;
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; ++i) scanf("%d", &cost[i]);
		if(n == 1) printf("%d 1\n", cost[1]);
		else {
            memset(G, false, sizeof(G));
            memset(dp, -1, sizeof(dp));
            memset(ways, 0, sizeof(ways));
            for(int i = 1; i <= m; ++i) {
                scanf("%d%d", &u, &v);
                G[u][v] = G[v][u] = true;
                dp[(1<<(u-1))|(1<<(v-1))][u][v] = dp[(1<<(u-1))|(1<<(v-1))][v][u] = cost[u]*cost[v];
                ways[(1<<(u-1))|(1<<(v-1))][u][v] = ways[(1<<(u-1))|(1<<(v-1))][v][u] = 1;
            }
            solve(n);
		}
	}
	return 0;
}