占棋盘(最大流 dinic)

问题

分析

代码

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int MXN = 105, MXE = 20010;
int mp[MXN][MXN], l[MXN], c[MXN]; // 网格
int tot, m, n, k, head[MXN<<1], d[MXN<<1], cur[MXN<<1];   // 前向星
struct Edge{int to, nxt, w;}edge[MXE]; // 图边
void addEdge(int u, int v, int w){ // 加边
	Edge &e = edge[++tot];
	e.to = v, e.nxt = head[u], e.w = w, head[u] = tot;
}
void add(int u, int v, int w){ // 加原边 和 反向边
	addEdge(u, v, w), addEdge(v, u, 0);
}
// 构建层次图
bool bfs(int s, int t){
	int lst;
	queue<int> q;
	memset(d, 0, sizeof d);
	q.push(s), d[s] = 1, cur[s] = head[s];
	while(!q.empty()){
		lst = q.front(), q.pop();
		for(int i = head[lst]; ~i; i = edge[i].nxt){
			if(edge[i].w && !d[edge[i].to]){
				q.push(edge[i].to);
				cur[edge[i].to] = head[edge[i].to]; // 一个顶点出发的边是一个链表,初始化为链首
				d[edge[i].to] = d[lst]+1;
				if(edge[i].to == t) return true;
			}
		}		
	}
	return false;
}
// 多路增广
int dfs(int s, int t, int lim){
	if(s == t) return lim;
	int rest = lim; // 后续增广路的流量上界
	for(int flow, i = cur[s]; ~i && rest; i = edge[i].nxt, cur[s] = i){ // 当前弧优化
		if(edge[i].w && d[edge[i].to] == d[s]+1){
			flow = dfs(edge[i].to, t, min(edge[i].w, rest));
			if(!flow) d[edge[i].to] = 0; // 炸点
			edge[i].w -= flow, edge[i^1].w += flow, rest -= flow;
		}
	}
	return lim - rest;
}
int dinic(){
	int flow, maxflow = 0;
	while(bfs(m+n+1, m+n+2)) while(flow = dfs(m+n+1, m+n+2, inf)) maxflow += flow;
	return maxflow;
}
int main(){
	while(scanf("%d%d%d", &m, &n, &k) == 3){				
		for(int i = 1; i <= m; ++i) scanf("%d", l+i), l[i] = n - l[i];
		for(int i = 1; i <= n; ++i) scanf("%d", c+i), c[i] = m - c[i];
		memset(mp, 0, sizeof mp);
		for(int x, y, i = 1; i <= k; ++i) scanf("%d%d", &x, &y), --l[x], --c[y], mp[x][y] = 1;
		int jiong = 0;
		for(int i = 1; i <= m && !jiong; ++i) jiong = l[i] < 0 ? 1 : 0;
		for(int i = 1; i <= n && !jiong; ++i) jiong = c[i] < 0 ? 1 : 0;
		if(jiong) {printf("JIONG!\n"); continue;}
		tot = 1, memset(head, -1, sizeof head);
		// 空位约束边
		for(int i = 1; i <= m; ++i) for(int j = 1; j <= n; ++j) if(!mp[i][j]) add(i,m+j,1);
		for(int i = 1; i <= m; ++i) add(m+n+1, i, l[i]);   // s:m+n+1,行放弃人数上界约束边
		for(int i = 1; i <= n; ++i) add(m+i, m+n+2, c[i]); // t:m+n+2,列放弃人数上界约束边
		// 
		printf("%d\n", m*n-k-dinic());
	}	
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpphy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值