• NOIP2017宝藏


    题意

    link

    给出 \(n\) 个点 \(m\) 条边的图,选一个点作为根,每选一个点的价值是 \(\text{dep} \times w\), 即深度(从0开始)乘边权,求生成一棵树的最小价值。

    状压dp

    状态

    考虑到深度不好压缩,那就按照深度小到大往里加点,即统一深度的点一起放进去,这样深度就相同了。

    \(f[dep][S]\) 表示现在深度是 \(dep\), 选了集合 \(S\) 中的点的最小价值。

    枚举未加入的点,但是不知道它能接在哪些加入的点下面,怎么办?再多记一维状态就会空间爆炸。

    其实只要对每个选出的点连入已经加入的点就好,并且选最小的边权。

    因为如果深度不是当前的 \(dep\), 那么一定存在一种方案 \(dep\) 会更小,那么就更优。

    初始和最终状态

    \(f[0][2^i] = 0\) 表示选一个根。
    其它都是 无穷大。

    最后枚举深度, 就是最小的\(f[dep][U]\)

    转移

    \[f[dep][S] = \min_{T \cap S = \emptyset} \{ f[dep - 1][T] + \sum_{i \in T}w(i, S)\} \]

    其中 \(w(i, S)\) 表示点 \(i\) 连入 \(S\) 中的点的最小花费,预处理即可。

    分析

    预处理枚举子集和枚举连入的边,时间是 \(O(n2^n)\)

    转移枚举子集和补集再求和,总的时间复杂度是 \(O(n^23^n)\)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    
    using ll = long long;
    const int MAXN = 15;
    const int INF = 0x3f3f3f3f;
    //const int mod = 1000000007;
    int mod; 
    const double eps = 1e-9; 
    
    template <typename T>
    void Read(T &x) {
    	x = 0; T f = 1; char a = getchar();
    	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
    	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
    	x *= f;
    }
    
    inline int add(const int &a, const int &b) { 
    	static int c;
    	c = a + b;
    	if (c >= mod) c -= mod;
    	if (c < 0) c += mod;
    	return c;   
    } 
    inline int mul(const int &a, const int &b) {
    	return 1ll * a * b % mod; 
    }
    int qpow(int a, int b) {
    	int sum(1);
    	while(b) {
    		if (b & 1) sum = mul(sum, a);
    		a = mul(a, a);
    		b >>= 1;
    	}
    	return sum; 
    }
    
    int n, m;
    int val[MAXN][MAXN], dis[MAXN][ (1 << MAXN) + 10];
    
    int f[MAXN][ (1 << MAXN) + 10]; 
    int main() {
    	memset(val, 0x3f, sizeof(val));
    	cin >> n >> m;
    	for (int i = 1; i <= m; i ++) {
    		int u, v, w;
    		cin >> u >> v >> w;
    		val[u][v] = val[v][u] = min(val[u][v], w); 
    	}
    	memset(dis, 0x3f, sizeof(dis)); 
    	for (int i = 1; i <= n; i ++)
    		for (int j = 1; j < (1 << n); j ++)
    			for (int k = 0; k < n; k ++)
    				if (j & (1 << k)) 
    					dis[i][j] = min(dis[i][j], val[i][k + 1]); 
    	
    	memset(f, 0x3f, sizeof(f));
    	for (int i = 1; i <= n; i ++) 
    		f[0][1 << i - 1] = 0;
    	int U = (1 << n) - 1; 
    	for (int i = 1; i < n; i ++) 
    		for (int j = 1; j < (1 << n); j ++)	{
    			int now = U ^ j; 
    			if (f[i - 1][now] == INF)
    				continue;
    			for (int k = j; k; k = j & (k - 1)) {
    				int sum = 0; bool p = 0;
    				for (int x = 0; x < n; x ++)
    					if (k & (1 << x)) {
    						if (dis[x + 1][now] == INF) {
    							p = 1; break;
    						} else 
    							sum += dis[x + 1][now] * i; 
    					}
    				if (p) continue;
    				f[i][now | k] = min(f[i][now | k], f[i - 1][now] + sum);	
    			} 
    		}
    	int ans = INF;
    	for (int i = 0; i < n; i ++)
    		ans = min(ans, f[i][U]);
    	cout << ans; 
    	return 0;
    } 
    
  • 相关阅读:
    Win32 开发
    Corners in C#
    swfupload在IE8下显示正常,但是单击添加按钮无反应
    Windows Script Host(WSH)
    研磨设计模式 之 中介者模式(Mediator)
    Pure GPU Computing Platform : NVIDIA CUDA Tutorial
    BattleField 2142引擎图形程序员小访谈
    利用SAH实现kD树快速分割模型实践
    给大家看一下德国的家居装潢技术,在装修房子的朋友可以欣赏一下
    Python与Microsoft Office自动化操作
  • 原文地址:https://www.cnblogs.com/qjbqjb/p/15948543.html
Copyright © 2020-2023  润新知