NOIP2017 宝藏 (状压dp)

题目等价于给定无向图, 求一棵有根生成树, 假设树的根为$rt$, 对于树上的边$(u,v,w)$, 花费为$dep[v]*w$, 使得总花费最小.

设$dp[S][d]$为当前集合为$S$, 树的深度为$d$时的最小花费.

有$dp[S][d]=min{dp[S'][d-1]+cost[S'][S]}$

$S'$是$S$的子集, $cost$可以预处理, 总的复杂度是$O(n^22^n+n3^n)$

#include <iostream>
#include <cstdio>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;

const int N = 12, INF = 0x3f3f3f3f;
int n, m, a[N][N];
int dp[1<<N][N], f[1<<N][N];
int c[1<<N][1<<N];

int main() {
	scanf("%d%d", &n, &m);
	memset(a,INF,sizeof a);
	REP(i,1,m) {
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		--u,--v;
		a[u][v]=a[v][u]=min(a[u][v],w);
	}
	int mx = (1<<n)-1;
	memset(f,INF,sizeof f);
	REP(s,1,mx) REP(i,0,n-1) if (s>>i&1) { 
		f[s][i] = 0;
		REP(j,0,n-1) if (s>>j&1^1) f[s][j] = min(f[s][j], a[i][j]);
	}
	memset(dp,INF,sizeof dp);
	REP(i,0,n-1) dp[1<<i][0] = 0;
	REP(d,1,n-1) REP(i,1,mx) {
		for (int j=i-1&i; j; --j&=i) if (dp[j][d-1]!=INF) {
			if (!c[i][j]) {
				long long sum = 0;
				REP(k,0,n-1) sum += (i>>k&1)*f[j][k];
				if (sum>=INF) c[i][j] = INF;
				else c[i][j] = sum;
			}
			if (c[i][j]==INF) continue;
			dp[i][d] = min(dp[i][d], dp[j][d-1]+c[i][j]*d);
		}
	}
	int ans = INF;
	REP(i,0,n-1) ans = min(ans, dp[mx][i]);
	printf("%d
", ans);
}
原文地址:https://www.cnblogs.com/uid001/p/11041310.html