zoukankan      html  css  js  c++  java
  • Codeforces 152E Garden (状压dp+最短路)

    题意:

    有一个 (n*m) 的花园,每个位置有一定数量的花,有 (k) 个建筑,然后要把这些建筑连通起来,连通就必须要把花园的一些花杀死,铺上道路

    问最少通过杀死多少花可以使建筑连通

    乍一看很像最小生成树,但是确发现并不是

    带权并查集? 好像也不是

    那怎么维护连通性? 懵逼。。。。

    我们发现最后建筑连成的路必定是成树形的

    那么我们只要断掉一条边必定能够使得树变成两半

    (k) 很小,那么是否可以暴力维护有哪些建筑已经在一个连通块中?

    很容易想到把建筑物状态压缩,然后 (dp)

    (dp[mask]) 表示包含建筑物集合为 (mask) 的最小花费

    然后合并两个 (mask_1)(mask_2) 必须满足 (mask_1~&~mask_2=0),不然并不需要合并;

    合并的代价是什么? 这是个问题,因为合并两个块,我们不一定是从建筑物连向建筑物的,有可能是内部的一个点

    那怎么办?

    退一步,再记录一维 (dp[i][mask]) 表示包含 (i) 且建筑物集合为 (mask) 的最小花费

    那么这样就好计算费用了,(dp[i][mask_1])(dp[j][mask_2]) 合并的费用就是 (i)(j) 的最短路,可以 (bfs) 实现

    然后就没了吧

    记录路径开个数组保存

    小细节看代码注释

    #include <map>
    #include <set>
    #include <ctime>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <vector>
    #include <bitset>
    #include <cstdio>
    #include <cctype>
    #include <string>
    #include <numeric>
    #include <cstring>
    #include <cassert>
    #include <climits>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std ;
    //#define int long long
    #define rep(i, a, b) for (int i = (a); i <= (b); i++)
    #define per(i, a, b) for (int i = (a); i >= (b); i--)
    #define loop(s, v, it) for (s::iterator it = v.begin(); it != v.end(); it++)
    #define cont(i, x) for (int i = head[x]; i; i = e[i].nxt)
    #define clr(a) memset(a, 0, sizeof(a))
    #define ass(a, sum) memset(a, sum, sizeof(a))
    #define lowbit(x) (x & -x)
    #define all(x) x.begin(), x.end()
    #define ub upper_bound
    #define lb lower_bound
    #define pq priority_queue
    #define mp make_pair
    #define pb push_back
    #define fi first
    #define se second
    #define iv inline void
    #define enter cout << endl
    #define siz(x) ((int)x.size())
    #define file(x) freopen(#x".in", "r", stdin),freopen(#x".out", "w", stdout)
    typedef long long ll ;
    typedef unsigned long long ull ;
    typedef pair <int, int> pii ;
    typedef vector <int> vi ;
    typedef vector <pii> vii ;
    typedef queue <int> qi ;
    typedef queue <pii> qii ;
    typedef set <int> si ;
    typedef map <int, int> mii ;
    typedef map <string, int> msi ;
    const int N = 210 ;
    const int M = 1 << 7 ;
    const int INF = 0x3f3f3f3f ;
    const int iinf = 1 << 30 ;
    const ll linf = 2e18 ;
    const int MOD = 1000000007 ;
    const double eps = 1e-7 ;
    void print(int x) { cout << x << endl ; exit(0) ; }
    void PRINT(string x) { cout << x << endl ; exit(0) ; }
    void douout(double x){ printf("%lf
    ", x + 0.0000000001) ; }
    
    int dp[N][M], pre[N][M], vis[N][M] ;
    // dp[i][j] 表示从某个格子出发访问过j状态个格子最小花费
    int hsh[N], maz[N][N] ;
    char g[N][N] ;
    int n, m, k, nn, mm ;
    int dx[] = {0, 0, -1, 1} ;
    int dy[] = {-1, 1, 0, 0} ;
    
    struct node {
    	int x, dis ;
    } ;
    
    queue <node> q ;
    
    void modify(int x, int dis, int w, int fa) {
    	if (dp[x][dis] > w) {
    		dp[x][dis] = w ; pre[x][dis] = fa ;
    		if (!vis[x][dis]) {
    			q.push((node) {x, dis}) ;
    			vis[x][dis] = 1 ;
    		}
    	}
    }
    
    void dfs(int X, int dis) {
    	int x = X / m, y = X % m ;
    	g[x][y] = 'X' ;
    	if (pre[X][dis] == -1) return ;
    	int to = pre[X][dis] / 1000, diss = pre[X][dis] % 1000 ;
    	dfs(to, diss) ;
    	if (diss - dis) dfs(to, dis - diss) ;
    }
    
    bool check(int x, int y) {
    	if (x >= 0 && x < n && y >= 0 && y < m) return true ;
    	return false ;
    }
    
    void getans() {
    	while (!q.empty()) { // 跑bfs求最短路,同时求出 dp 数组
    		node nw = q.front() ; q.pop() ;
    		int now = nw.x, x = nw.x / m, y = nw.x % m, dis = nw.dis ;
    		vis[now][dis] = false ;
    		rep(i, 0, 3) {
    			int tx = x + dx[i], ty = y + dy[i] ;
    			if (!check(tx, ty)) continue ;
    			int to = tx * m + ty ;
    			modify(to, dis, dp[now][dis] + maz[tx][ty], now * 1000 + dis) ;
    		}
    		int t = mm - dis - 1 ;
    		for (int i = t; i; i = (i - 1) & t) { // 取另外与之有交集的连通块进行合并
    			// maz[x][y] 多算了一次减去
    			modify(now, i | dis, dp[now][i] + dp[now][dis] - maz[x][y], now * 1000 + dis) ;
    		}
    	}
    	int ans = iinf, now ;
    	rep(i, 0, nn - 1)
    	if (ans > dp[i][mm - 1]) { // 找到最优出发点
    		ans = dp[i][mm - 1] ;
    		now = i ;
    	}
    	dfs(now, mm - 1) ;
    	printf("%d
    ", ans) ;
    	rep(i, 0, n - 1) {
    		rep(j, 0, m - 1) cout << g[i][j] ;
    		enter ;
    	}
    }
    
    signed main(){
    	scanf("%d%d%d", &n, &m, &k) ;
    	rep(i, 0, n - 1) {
    		rep(j, 0, m - 1) {
    			scanf("%d", &maz[i][j]) ;
    			g[i][j] = '.' ;
    		}
    	}
    	nn = n * m ; mm = (1 << k) ;
    	clr(hsh) ; clr(vis) ;
    	rep(i, 0, nn - 1)
    	rep(j, 0, mm - 1)
    	dp[i][j] = iinf ;
    	rep(i, 0, k - 1) {
    		int a, b ; scanf("%d%d", &a, &b) ; a-- ; b-- ;
    		int id = a * m + b ;
    		hsh[id] = 1 << i ;
    		modify(id, hsh[id], maz[a][b], -1) ;
    	}
    	getans() ;
    	return 0 ;
    }
    
    /*
    写代码时请注意:
    	1.ll?数组大小,边界?数据范围?
    	2.精度?
    	3.特判?
    	4.至少做一些
    思考提醒:
    	1.最大值最小->二分?
    	2.可以贪心么?不行dp可以么
    	3.可以优化么
    	4.维护区间用什么数据结构?
    	5.统计方案是用dp?模了么?
    	6.逆向思维?
    */
    
    
    
    加油ヾ(◍°∇°◍)ノ゙
  • 相关阅读:
    我的WCF之旅(1):创建一个简单的WCF程序
    网页设计中颜色的搭配
    CSS HACK:全面兼容IE6/IE7/IE8/FF的CSS HACK
    UVa 1326 Jurassic Remains
    UVa 10340 All in All
    UVa 673 Parentheses Balance
    UVa 442 Matrix Chain Multiplication
    UVa 10970 Big Chocolate
    UVa 679 Dropping Balls
    UVa 133 The Dole Queue
  • 原文地址:https://www.cnblogs.com/harryhqg/p/10466097.html
Copyright © 2011-2022 走看看