• [BZOJ2595][Wc2008]游览计划


    [BZOJ2595][Wc2008]游览计划

    试题描述

    输入

    第一行有两个整数,N和 M,描述方块的数目。 
    接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
    否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,
    行首行末也可能有多余的空格。

    输出

    由 N + 1行组成。第一行为一个整数,表示你所给出的方案
    中安排的志愿者总数目。 
    接下来 N行,每行M 个字符,描述方案中相应方块的情况: 
    z  ‘_’(下划线)表示该方块没有安排志愿者; 
    z  ‘o’(小写英文字母o)表示该方块安排了志愿者; 
    z  ‘x’(小写英文字母x)表示该方块是一个景点; 
    注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不
    一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。

    输入示例

    4 4
    0 1 1 0
    2 5 5 1
    1 5 5 1
    0 1 1 0

    输出示例

    6

    数据规模及约定

    对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内

    题解

    听说叫斯坦纳树,其实是一个状压 dp。这个 dp 挺奇怪的,大概是设 f[i][j][S] 表示包含了 (i, j) 的连通块中至少包含集合 S 中所有景点的最小代价。具体请看这里

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <queue>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 11
    #define maxs 1030
    #define oo 2147483647
    
    int n, m, K, A[maxn][maxn], f[maxn][maxn][maxs];
    
    struct Sta {
    	int x, y, S;
    	Sta(): x(0), y(0), S(0) {}
    	Sta(int _1, int _2, int _3): x(_1), y(_2), S(_3) {}
    } fa[maxn][maxn][maxs];
    
    bool up(Sta to, Sta from, int val) {
    	if(f[to.x][to.y][to.S] > val) {
    		f[to.x][to.y][to.S] = val, fa[to.x][to.y][to.S] = from;
    		return 1;
    	}
    	return 0;
    }
    
    queue <Sta> Q;
    bool inq[maxn][maxn];
    void init() {
    	memset(inq, 0, sizeof(inq));
    	while(!Q.empty()) Q.pop();
    	return ;
    }
    int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
    bool inside(Sta x) {
    	return 1 <= x.x && x.x <= n && 1 <= x.y && x.y <= m;
    }
    void expend() {
    	while(!Q.empty()) {
    		Sta u = Q.front(); Q.pop(); inq[u.x][u.y] = 0;
    		for(int i = 0; i < 4; i++) {
    			Sta v(u.x + dx[i], u.y + dy[i], u.S);
    			if(inside(v) && up(v, u, f[u.x][u.y][u.S] + A[v.x][v.y]) && !inq[v.x][v.y])
    				inq[v.x][v.y] = 1, Q.push(v);
    		}
    	}
    	return ;
    }
    
    bool put[maxn][maxn];
    void Path(Sta u) {
    	if(!u.S) return ;
    	put[u.x][u.y] = 1;
    	Sta v = fa[u.x][u.y][u.S];
    	Path(v);
    	if(v.S != u.S && v.S) Path(Sta(v.x, v.y, u.S - v.S));
    	return ;
    }
    
    int main() {
    	n = read(); m = read();
    	
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++)
    			for(int S = 0; S < maxs; S++)
    				f[i][j][S] = oo;
    	int p = 0;
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++) {
    			A[i][j] = read();
    			if(!A[i][j]) f[i][j][1<<p++] = 0;
    		}
    	int all = (1 << p) - 1;
    	for(int S = 1; S <= all; S++) {
    		init();
    		for(int i = 1; i <= n; i++)
    			for(int j = 1; j <= m; j++) {
    				for(int Sub = S & S - 1; Sub; Sub = S & Sub - 1)
    					if(f[i][j][Sub] < oo && f[i][j][S-Sub] < oo)
    						up(Sta(i, j, S), Sta(i, j, Sub), f[i][j][Sub] + f[i][j][S-Sub] - A[i][j]);
    				if(f[i][j][S] < oo) Q.push(Sta(i, j, S)), inq[i][j] = 1;
    			}
    		expend();
    	}
    	
    	for(int i = 1; i <= n; i++) {
    		bool has = 0;
    		for(int j = 1; j <= m; j++) if(!A[i][j]) {
    			printf("%d
    ", f[i][j][all]);
    			Path(Sta(i, j, all));
    			for(int a = 1; a <= n; a++) {
    				for(int b = 1; b <= m; b++)
    					if(!A[a][b]) putchar('x');
    					else if(put[a][b]) putchar('o');
    					else putchar('_');
    				putchar('
    ');
    			}
    			has = 1; break;
    		}
    		if(has) break;
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    BZOJ#2820. YY的GCD
    BZOJ#3771. Triple(FFT+生成函数)
    BZOJ#4503. 两个串
    HDU#4609. 3-idiots
    UVA 12298 Super Poker II(FFT+母函数)
    HDU#1402. A×B
    树状数组的区间修改与区间查修
    COCI. DIFERENCIJA(序列处理中的小技巧)
    dt.select默认不区分大小写
    缓存,缓存算法和缓存框架
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6610879.html
Copyright © 2020-2023  润新知