• [WC2008]游览计划 「斯坦那树模板」


    斯坦那树

    百度释义

    斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。

    即最小斯坦那树即为并非选择所有的结点,而是选择一部分结点,为保证它们连通,且求解最小开销

    题解

    斯坦那树模板

    发现直接表示点的存在性没有意义

    设函数 (f[i][state]) 表示:对于点 (i),其它结点与其连通情况

    那么有两种转移

    其一、由其子集转移

    [f[i][state] = minlimits_{sub in state} {f[i][sub] + f[i][complement_{state}sub] - value_i} ]

    之所以要减去 (value_i) 是因为会算重

    附:枚举子集的方法

    for (int sub = state & (state - 1); sub; sub = (sub - 1) & state)
    

    其二、由相邻当前状态下结点转移

    [f[i][state] = minlimits_{state_p = true} {f[p][state] + value_i} ]

    发现很像三角形不等式,故考虑 (SPFA) 转移

    总复杂度 (O (n3^n + kE2^n))(3^n) 为枚举子集总复杂度

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    
    using namespace std;
    
    const int MAXN = 10 + 5;
    const int MAXM = 1 << 10;
    
    const int INF = 0x3f3f3f3f;
    
    const int NextX[4]= {- 1, 0, 0, 1}, NextY[4]= {0, - 1, 1, 0};
    
    int N, M;
    int Map[MAXN][MAXN]= {0};
    
    struct preSt {
    	int x, y;
    	int state;
    
    	preSt (int fx = 0, int fy = 0, int fs = 0) :
    		x (fx), y (fy), state (fs) {}
    } ;
    
    int f[MAXN][MAXN][MAXM];
    preSt pre[MAXN][MAXN][MAXM];
    int cnt = 0;
    
    queue<pair<int, int> > que;
    void SPFA (int state) {
    	while (! que.empty()) {
    		pair<int, int> top = que.front();
    		que.pop();
    
    		int x = top.first, y = top.second;
    		for (int i = 0; i < 4; i ++) {
    			int tx = x + NextX[i];
    			int ty = y + NextY[i];
    			if (tx < 1 || tx > N || ty < 1 || ty > M)
    				continue;
    			if (f[x][y][state] + Map[tx][ty] < f[tx][ty][state]) {
    				f[tx][ty][state] = f[x][y][state] + Map[tx][ty];
    				pre[tx][ty][state] = preSt (x, y, state);
    				que.push(make_pair (tx, ty));
    			}
    		}
    	}
    }
    
    int tag[MAXN][MAXN]= {0};
    void traceback (int x, int y, int state) {
    	if (! x || ! y)
    		return ;
    	tag[x][y] = 1;
    	preSt pr = pre[x][y][state];
    	traceback (pr.x, pr.y, pr.state);
    	if (pr.x == x && pr.y == y)
    		traceback (pr.x, pr.y, state - pr.state);
    }
    
    int getnum () {
    	int num = 0;
    	char ch = getchar ();
    
    	while (! isdigit (ch))
    		ch = getchar ();
    	while (isdigit (ch))
    		num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
    
    	return num;
    }
    
    int main () {
    	memset (f, 0x3f, sizeof (f));
    	N = getnum (), M = getnum ();
    	int px, py;
    	for (int i = 1; i <= N; i ++)
    		for (int j = 1; j <= M; j ++) {
    			Map[i][j] = getnum ();
    			if (! Map[i][j]) {
    				cnt ++, f[i][j][1 << (cnt - 1)] = 0;
    				px = i, py = j;
    			}
    		}
    	int limit = (1 << cnt) - 1;
    	for (int state = 1; state <= limit; state ++) {
    		for (int i = 1; i <= N; i ++)
    			for (int j = 1; j <= M; j ++) {
    				for (int sub = state & (state - 1); sub; sub = (sub - 1) & state) // from subset
    					if (f[i][j][sub] + f[i][j][state - sub] - Map[i][j] < f[i][j][state]) {
    						f[i][j][state] = f[i][j][sub] + f[i][j][state - sub] - Map[i][j];
    						pre[i][j][state] = preSt (i, j, sub);
    					}
    				if (f[i][j][state] < INF)
    					que.push(make_pair (i, j));
    			}
    		SPFA (state); // from other nodes
    	}
    	traceback (px, py, limit);
    	printf ("%d
    ", f[px][py][limit]);
    	for (int i = 1; i <= N; i ++) {
    		for (int j = 1; j <= M; j ++) {
    			if (! Map[i][j])
    				putchar ('x');
    			else {
    				tag[i][j] ? putchar ('o') : putchar ('_');
    			}
    		}
    		puts ("");
    	}
    
    	return 0;
    }
    
    /*
    4 4
    0 1 1 0
    2 5 5 1
    1 5 5 1
    0 1 1 0
    */
    
  • 相关阅读:
    tomcat---基本知识点解读;配置文件详解
    nginx常用配置参数介绍-upstream
    nginx配置文件-详解
    nginx简单介绍
    GCC编译已经引入math.h文件提示cos,sin等函数未定义
    进程与进程间通信(3)
    进程与进程间通信(1)
    进程与进程间通信(2)
    primer_c++_迭代器
    primer_C++_3.5 数组
  • 原文地址:https://www.cnblogs.com/Colythme/p/10328442.html
Copyright © 2020-2023  润新知