zoukankan      html  css  js  c++  java
  • [洛谷P4311]士兵占领

    题目大意:有一个$n imes m$的棋盘,有的格子是障碍。要选择一些格子来放置士兵,一个空格子里可放一个士兵。希望第$i$行至少放置了$l_i$个士兵, 第$j$列至少放置了$c_j$个士兵。要求使用最少个数的士兵。输出个数。

    题解:先考虑无解,只要所有能放士兵的地方都放上了士兵,仍然存在行或列不满足限制条件,即为无解。

    逆向思考,把求使用最少的士兵转化为,放满士兵后,最多能删掉多少个士兵。网络流。

    给每一行每一列分别建一个点,对于所有非障碍坐标$(x,y)$,从$x$行向$y$列连一条容量为$1$的边,表示可以删除一个士兵。

    源点向每一行连边,容量为这一行能删除的士兵个数的最大值(即这一行可以放得士兵数减去这一行需要的士兵数)。

    列也是这样。跑最大流,每一个单位的流量表示删除一个士兵,从总士兵个数中删除即可

    卡点:

    C++ Code:

    #include <cstdio>
    #include <cstring>
    #define maxn 111
    using namespace std;
    const int inf = 0x3f3f3f3f;
    int n, m, k, sum;
    int l[maxn], c[maxn];
    int L[maxn], C[maxn];
    bool v[maxn][maxn];
    
    int head[maxn << 1], cnt = 2;
    struct Edge {
    	int to, nxt, w;
    } e[maxn * maxn << 1];
    void add(int a, int b, int c) {
    	e[cnt] = (Edge) {b, head[a], c}; head[a] = cnt;
    	e[cnt ^ 1] = (Edge) {a, head[b], 0}; head[b] = cnt ^ 1;
    	cnt += 2;
    }
    
    inline int min(int a, int b) {return a < b ? a : b;}
    int st, ed;
    int d[maxn << 1];
    int q[maxn << 1], h, t;
    inline bool bfs() {
    	memset(d, 0, sizeof d);
    	d[q[h = t = 0] =st] = 1;
    	while (h <= t) {
    		int u = q[h++];
    		for (int i = head[u]; i; i = e[i].nxt) {
    			int v = e[i].to;
    			if (!d[v] && e[i].w) {
    				d[v] = d[u] + 1;
    				q[++t] = v;
    			}
    		}
    	}
    	return d[ed];
    }
    int dfs(int x, int low) {
    	if (!low || x == ed) return low;
    	int res = 0, w, v;
    	for (int i = head[x]; i; i = e[i].nxt) {
    		v = e[i].to;
    		if ((d[v] == d[x] + 1) && e[i].w) {
    			w = dfs(v, min(low - res, e[i].w));
    			e[i].w -= w;
    			e[i ^ 1].w += w;
    			res += w;
    		}
    	}
    	if (!res) d[x] = -1;
    	return res;
    }
    void dinic() {
    	int ans = 0;
    	while (bfs()) ans += dfs(st, inf);
    	printf("%d
    ", sum - ans);
    }
    int main() {
    	scanf("%d%d%d", &n, &m, &k);
    	for (int i = 1; i <= n; i++) scanf("%d", &l[i]);
    	for (int i = 1; i <= m; i++) scanf("%d", &c[i]);
    	for (int i = 1; i < k; i++) {
    		int a, b;
    		scanf("%d%d", &a, &b);
    		v[a][b] = true;
    	}
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j <= m; j++) {
    			L[i] += !v[i][j];
    			C[j] += !v[i][j];
    			if (i == n) {
    				if (C[j] < c[j]) {
    					puts("JIONG!");
    					return 0;
    				}
    			}
    		}
    		if (L[i] < l[i]) {
    			puts("JIONG!");
    			return 0;
    		}
    		sum += L[i];
    	}
    	st = 0, ed = n + m + 1;
    	for (int i = 1; i <= n; i++) {
    		add(st, i, L[i] - l[i]);
    	}
    	for (int i = 1; i <= m; i++) {
    		add(i + n, ed, C[i] - c[i]);
    	}
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j <= m; j++) {
    			if (!v[i][j]) add(i, j + n, 1);
    		}
    	}
    	dinic();
    	return 0;
    }
    

      

  • 相关阅读:
    全程图解】ADSL+笔记本电脑 组建WIFI网络让5800实现WIFI上网(更新完毕)
    JSP用户管理系统【上学应付作业用】
    c++按位操作符
    F#: .NET中的函数编程语言
    Visual Studio OpenGL 配置方法
    Linux下挂载U盘方法
    开发者该以什么为骄傲
    POSIX约定与GNU长选项
    修复移动硬盘"文件或目录损坏且无法读取"
    某国外论坛关于什么是Computer Science的争论,你怎么看?
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/9481268.html
Copyright © 2011-2022 走看看