• 斯坦那树学习笔记


    引子

    最小斯坦纳树
    大概意思就是一个图给出 (k(k le 10)) 个关键点,要求选出若干条边使得这 (k) 个关键点连通,求边权和的最小值

    (Analysis)

    发现 (k) 很小,考虑状压 (dp)
    为得到最优解,我们需要考虑以每个点为根的形态
    (f_{i,S}) 表示以 (i) 为根关键点的状态为 (S) 时的最优解
    那么两个转移
    一、 (f_{i,S} = min(f_{i,S},f_{i,S0}+f_{i,S1}))
    表示用 (i) 这个根将两个不相交的状态直接合并在一起
    二、 (f_{i,S} = min(f_{i,S},f_{k,S}+e(k,i)))
    表示用之前的一个以 (k) 为根的状态连一条边到 (i) 变为以 (i) 为根
    我们发现第二种转移无法确定最佳的 (i,k) 的顺序,即 (dp) 用后效性
    这是考虑用最短路算法转移即可

    (Code)

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<iostream>
    using namespace std;
    
    const int N = 105, INF = 0x3f3f3f3f;
    int n, m, k, g[N][N], key[N], h[N], f[N][1045], vis[N];
    
    struct edge{
    	int to, nxt, w;
    }e[N * 10];
    inline void add(int u, int v, int w)
    {
    	static int tot = 0;
    	e[++tot] = edge{v, h[u], w}, h[u] = tot;
    }
    
    queue<int> Q;
    inline void spfa(int s)
    {
    	while (!Q.empty())
    	{
    		int now = Q.front(); Q.pop();
    		for(int i = h[now]; i; i = e[i].nxt)
    		if (f[e[i].to][s] > f[now][s] + e[i].w)
    		{
    			f[e[i].to][s] = f[now][s] + e[i].w;
    			if (!vis[e[i].to]) vis[e[i].to] = 1, Q.push(e[i].to);
    		}
    		vis[now] = 0;
    	}
    }
    inline void Stenir_Tree()
    {
    	for(int s = 1; s < (1 << k); s++)
    	{
    		memset(vis, 0, sizeof vis);
    		for(int i = 1; i <= n; i++)
    		{
    			for(int t = (s - 1) & s; t; t = (t - 1) & s) 
    				f[i][s] = min(f[i][s], f[i][t] + f[i][s ^ t]);
    			if (f[i][s] != INF) vis[i] = 1, Q.push(i);
    		}
    		spfa(s);
    	}
    }
    
    int main()
    {
    	memset(g, INF, sizeof g), memset(f, INF, sizeof f);
    	scanf("%d%d%d", &n, &m, &k);
    	for(int i = 1, x, y, z; i <= m; i++) 
    		scanf("%d%d%d", &x, &y, &z), g[x][y] = g[y][x] = min(g[x][y], z);
    	for(int i = 1; i <= n; i++)
    		for(int j = i + 1; j <= n; j++)
    		if (g[i][j] != INF) add(i, j, g[i][j]), add(j, i, g[i][j]);
    	for(int i = 0; i < k; i++) scanf("%d", &key[i]), f[key[i]][1 << i] = 0;
    	Stenir_Tree();
    	int ans = INF;
    	for(int i = 1; i <= n; i++) ans = min(ans, f[i][(1 << k) - 1]);
    	printf("%d
    ", ans);
    }
    
  • 相关阅读:
    [收藏转贴]struct探索·extern "C"含义探索 ·C++与C的混合编程·C 语言高效编程的几招
    [收藏转贴]构建RESTful风格的WCF服务
    [转贴] C/C++中动态链接库的创建和调用
    [转贴]WebService的简单实现 C++
    谷歌已经对Android的开源严防死守
    如何成为一名黑客?
    当程序员思路被打断
    编辑语言是这样的
    开发者需要掌握多少门语言?
    程序员的六大优势
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/14983532.html
Copyright © 2020-2023  润新知