zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    从未来过绍兴的小D有幸参加了Winter Camp 2008,他被这座历史名城的秀从未来过绍兴的小D有幸参加了Winter Camp 2008,他被这座历史名城的秀丽风景所吸引,强烈要求游览绍兴及其周边的所有景点。

    主办者将绍兴划分为N行M列(NXM)个方块,景点含于方块内,且一个方块至多有一个景点。无景点的方块视为路。

    为了保证安全与便利,主办方依据路况和治安状况,在非景点的一些方块内安排不同数量的志愿者:在景点内聘请导游(导游不是志愿者)。在选择旅游方案时,保证任意两个景点之间,存在一条路径,在这条路径所经过的每一个方块都有志愿者或者该方块为景点。既能满足选手们游览的需要,又能够让志愿者的总数最少。

    现在,希望你能够帮助主办方找到一种最好的安排方案。

    原题传送门。

    @solution@

    斯坦纳树经典题。

    斯坦纳树的定义是:给定一个图与 k 个特殊点,求将这 k 个特殊点连通的最小生成树。
    当 k = 点数时,就是常见的最小生成树的定义。

    状压 dp:记 dp[i][s] 表示当前点为 i,已经包含了 k 个特殊点的集合为 s,最小连通代价。
    第一类转移:dp[i][s] = min{dp[i][t] + dp[i][s xor t]},即两个集合取并集。
    第二类转移:dp[i][s] = min{dp[j][s] + dis(i, j)},即任意加一条边。
    正确性很显然,因为每一种可行方案都会被上面的过程考虑到。

    第二类转移带环,但是注意到这个转移很像最短路的 “松弛” 操作,于是我们直接写个 spfa 即可。
    时间复杂度(如果把 spfa 算作 O(nm) 的上界的话)为 O(n*3^k + nm*2^k)。

    本题因为是点权,为了不使第一种转移出现重复,dp[i][s] 不包含 i 的点权。

    @accepted code@

    #include <queue>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef pair<int, int> pii;
    #define mp make_pair
    #define fi first
    #define se second
    
    const int INF = (1 << 30);
    const int dx[] = {0, 1, -1, 0, 0};
    const int dy[] = {0, 0, 0, -1, 1};
    
    bool inq[10][10];
    int A[10][10], id[10][10], N, M, K, S;
    int dp[10][10][1 << 10], pre[10][10][1 << 10];
    queue<pii>que;
    void run(int s) {
    	for(int i=0;i<N;i++)
    		for(int j=0;j<M;j++)
    			que.push(mp(i, j)), inq[i][j] = true;
    	while( !que.empty() ) {
    		pii p = que.front(); que.pop(); inq[p.fi][p.se] = false;
    		for(int i=1;i<=4;i++) {
    			int x0 = p.fi + dx[i], y0 = p.se + dy[i];
    			if( x0 >= N || y0 >= M || x0 < 0 || y0 < 0 ) continue;
    			if( dp[x0][y0][s] > dp[p.fi][p.se][s] + A[p.fi][p.se] ) {
    				dp[x0][y0][s] = dp[p.fi][p.se][s] + A[p.fi][p.se];
    				pre[x0][y0][s] = i;
    				if( !inq[x0][y0] ) {
    					que.push(mp(x0, y0));
    					inq[x0][y0] = true;
    				}
    			}
    		}
    	}
    }
    void solve() {
    	for(int s=1;s<S;s++) {
    		for(int i=0;i<N;i++)
    			for(int j=0;j<M;j++) {
    				int t = s & (s - 1);
    				while( t ) {
    					if( dp[i][j][s] > dp[i][j][t] + dp[i][j][s ^ t] ) {
    						dp[i][j][s] = dp[i][j][t] + dp[i][j][s ^ t];
    						pre[i][j][s] = -t;
    					}
    					t = s & (t - 1);
    				}
    			}
    		run(s);
    	}
    }
    bool tag[10][10];
    void get(int x, int y, int s) {
    	tag[x][y] = true;
    	if( pre[x][y][s] > 0 )
    		get(x - dx[pre[x][y][s]], y - dy[pre[x][y][s]], s);
    	else if( pre[x][y][s] < 0 ) {
    		pre[x][y][s] = -pre[x][y][s];
    		get(x, y, pre[x][y][s]), get(x, y, s^pre[x][y][s]);
    	}
    }
    
    int main() {
    	scanf("%d%d", &N, &M);
    	for(int i=0;i<N;i++)
    		for(int j=0;j<M;j++) {
    			scanf("%d", &A[i][j]);
    			if( A[i][j] == 0 ) id[i][j] = (K++);
    		}
    	S = (1 << K);
    	for(int i=0;i<N;i++)
    		for(int j=0;j<M;j++) {
    			for(int s=0;s<S;s++) dp[i][j][s] = INF;
    			if( A[i][j] == 0 ) dp[i][j][1 << id[i][j]] = 0;
    		}
    	solve();
    	int x, y, ans = INF;
    	for(int i=0;i<N;i++)
    		for(int j=0;j<M;j++)
    			if( ans > dp[i][j][S - 1] + A[i][j] )
    				ans = dp[i][j][S - 1] + A[i][j], x = i, y = j;
    	printf("%d
    ", ans);
    	get(x, y, S - 1);
    	for(int i=0;i<N;i++) {
    		for(int j=0;j<M;j++) {
    			if( tag[i][j] ) {
    				if( A[i][j] == 0 )
    					putchar('x');
    				else putchar('o');
    			}
    			else putchar('_');
    		}
    		puts("");
    	}
    }
    /*
    8 8
    1 4 1 3 4 2 4 1
    4 3 1 2 0 1 2 3
    3 2 1 3 0 3 1 2
    2 6 5 0 2 4 1 0
    5 1 2 1 3 4 2 5
    5 1 3 1 5 0 1 4
    5 0 6 1 4 5 3 4
    0 2 2 2 3 4 1 1
    */
    

    @details@

    关于第二类转移,其实也可以用 floyd 先预处理出所有点对的最短路,然后直接点对两两 O(n^2) 转移。

    因为 spfa 的复杂度上界是 O(NM),对于 M 比较大的稠密图用 floyd 更快。

    当然对于本题这种网格图 spfa 的复杂度和 floyd 没差,spfa 的常数说不定还要小一些。

  • 相关阅读:
    简明Python3教程 12.问题解决
    简明Python3教程 11.数据结构
    【SPOJ 694】Distinct Substrings
    【codeforces Manthan, Codefest 17 C】Helga Hufflepuff's Cup
    【CF Manthan, Codefest 17 B】Marvolo Gaunt's Ring
    【CF Manthan, Codefest 17 A】Tom Riddle's Diary
    【SPOJ 220】 PHRASES
    【POJ 3261】Milk Patterns
    【POJ 3294】Life Forms
    【POJ 1226】Substrings
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12418981.html
Copyright © 2011-2022 走看看