分析
在变化的量是啥?已经打的点的集合,还有当前生成树的深度。
 于是我们用      f[s][i] 表示已打的集合为      s , 生成树深度为      i 的最小代价。
 转移的话,答案肯定是由      S 的子集转移而来,我们就枚举子集,剩下的点作为第      i 层,以此得到转移方程:
      f[S][i]=min(f[S0][i−1]+i∗v[S0][S^     S0]) (     S0 是      S 的子集,     v[S1][S2] 表示 将      S2 的点加入生成树      S1 的最小距离)
 复杂度是     O(n3n)
证明
让我们思考一下为什么这样转移是对的吧。
 首先,肯定要有点作为第      i 层,我们就相当于枚举这些点,于是      f[S0][i−1] 就好理解了。
 然后,贪心的想,对于两个集合要连通,我们选择的边肯定都是小的边。
 需要理解的是,为什么代价是      i∗v[S0][S^     S0] 呢?有疑惑的点在于为什么乘      i。下面我们证明最优解一定会被覆盖到。
 这其实考验对子集的理解。假如我们在转移的时候用的边不应该在第      i 层,这条边连接的是      (a,b),其中      a∈S0,b∈S^     S0,那么我们在枚举到     S+{     b}的时候,这条边就不计入转移了,就不存在该问题了。也就是说,我们转移的时候得到的临时答案,都是大于等于最终答案的,不存在比答案小的情况,也不会漏解。
 不知道讲的清不清楚,有问题可以与我交流哈!!
upd
有一些状态是错误的,有一些状态是正确的,但是正确的状态比错误的状态优,因此最终答案肯定正确的。
代码如下
//f[i][s] = max(f[i-1][s0] + i * v[s0][s^s0])
#include <bits/stdc++.h>
#define inf 2147483647
using namespace std;
int f[12][1 << 12], v[1 << 12][1 << 12], g[1 << 12][12], mp[12][12], ans = inf;
int main(){
	int i, j, k, n, m, a, b, c, s, s0, sum;
	memset(f, 1, sizeof(f));
	memset(v, 1, sizeof(v));
	memset(g, 1, sizeof(g));
	memset(mp, 1, sizeof(mp));
	scanf("%d%d", &n, &m);
	for(i = 1; i <= m; i++){
		scanf("%d%d%d", &a, &b, &c);
		a--, b--;
		if(mp[a][b] > c) mp[a][b] = mp[b][a] = c;
	}
	/*for(i = 0; i < n; i++)
		for(j = 0; j < n; j++)
			if(mp[i][j]) v[1 << i][1 << j] = mp[i][j];*/
	for(i = 0; i < (1 << n); i++){
		for(j = 0; j < n; j++){
			if(1 << j & i) continue;
			for(k = 0; k < n; k++) if(1 << k & i) g[i][j] = min(g[i][j], mp[j][k]);
			//printf("===%d %d %d\n", i, j, g[i][j]);
		}
	}
	for(i = 0; i < (1 << n); i++){
		s = (1 << n) - 1 - i;
		for(j = s; j; j = (j - 1) & s){
			sum = 0;
			for(k = 0; k < n; k++) if(1 << k & j) sum += g[i][k];
			v[i][j] = min(v[i][j], sum);
			//printf("===============%d %d %d\n", i, j, v[i][j]);
		}
	}
	for(i = 0; i < n; i++) f[0][1 << i] = 0;
	for(i = 1; i < n; i++){
		for(s = 0; s < (1 << n); s++){
			for(s0 = s; s0; s0 = (s0 - 1) & s) f[i][s] = min(f[i][s], f[i - 1][s0] + i * v[s0][s ^ s0]);
			//printf("%d %d %d\n", i, s, f[i][s]);
		}
	}
	for(i = 0; i < n; i++) ans = min(ans, f[i][(1 << n) - 1]);
	printf("%d", ans);
	return 0;
}

 京公网安备 11010502036488号
京公网安备 11010502036488号