【BZOJ】1458: 士兵占领(上下界网络流)

http://www.lydsy.com/JudgeOnline/problem.php?id=1458

是不是我脑洞太小了。。。。。。。直接弄上下界最小流。。。。。。。。(就当复习了。。

二分图X和Y,然后如果(x,y)能放,那么连边x->y,上界1,下界0。

然后源s->x连下界为要求的下界,上界为oo

y->t连下界为要求下界,上界为oo。

然后来一次上下界最小流。。

 

//看了题解。。。感觉。。。。。。。。我脑洞怎么那么小。。。。将问题转换为:放满棋盘后去掉最多的士兵。搬运hzwer学长的题解:

此题的思路是先放满棋盘,然后考虑最多可以删多少个。。。

某一行和某一列的可以放的格子数小于需求就直接jiong掉
然后从源向每一行连边,流量为可以放的格子数 - 需求的格子数(也就是可以删多少格子)
从每一列向汇,同上.
从每一个非障碍的格子的行向列连边流量为1
跑一遍最大流即可ans=可放格子数-maxflow

囧。。

#include <cstdio>

#include <cstring>

#include <cmath>

#include <string>

#include <iostream>

#include <algorithm>

#include <queue>

#include <set>

#include <map>

using namespace std;

typedef long long ll;

#define rep(i, n) for(int i=0; i<(n); ++i)

#define for1(i,a,n) for(int i=(a);i<=(n);++i)

#define for2(i,a,n) for(int i=(a);i<(n);++i)

#define for3(i,a,n) for(int i=(a);i>=(n);--i)

#define for4(i,a,n) for(int i=(a);i>(n);--i)

#define CC(i,a) memset(i,a,sizeof(i))

#define read(a) a=getint()

#define print(a) printf("%d", a)

#define dbg(x) cout << (#x) << " = " << (x) << endl

#define error(x) (!(x)?puts("error"):0)

#define rdm(x, i) for(int i=ihead[x]; i; i=e[i].next)

inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }



const int N=305, oo=~0u>>1;

int ihead[N], cnt=1;

struct dat { int next, to, from, cap; }e[N*N*2];

void add(int u, int v, int c) {

	e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].from=u; e[cnt].to=v; e[cnt].cap=c;

	e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].from=v; e[cnt].to=u; e[cnt].cap=0;

}

int p[N], d[N], gap[N], cur[N];

int isap(int s, int t, int n) {

	for1(i, 0, n) p[i]=0, d[i]=0, gap[i]=0, cur[i]=ihead[i];

	gap[0]=n; int ret=0, f, u=s, i;

	while(d[s]<n) {

		for(i=cur[u]; i; i=e[i].next) if(e[i].cap && d[e[i].to]+1==d[u]) break;

		if(i) {

			p[e[i].to]=cur[u]=i; u=e[i].to;

			if(u==t) {

				for(f=oo; u!=s; u=e[p[u]].from) f=min(f, e[p[u]].cap);

				for(u=t; u!=s; u=e[p[u]].from) e[p[u]].cap-=f, e[p[u]^1].cap+=f;

				ret+=f;

			}

		}

		else {

			if(!(--gap[d[u]])) break;

			d[u]=n; cur[u]=ihead[u];

			for(i=ihead[u]; i; i=e[i].next) if(e[i].cap && d[e[i].to]+1<d[u]) d[u]=d[e[i].to]+1;

			++gap[d[u]];

			if(u!=s) u=e[p[u]].from;

		}

	}

	return ret;

}



int n, m, k, x[N], y[N], vis[N][N], in[N];

#define X(i) (i)

#define Y(i) (n+i)

int main() {

	read(n); read(m); read(k); int s=n+m+1, t=s+1;

	for1(i, 1, n) read(x[i]);

	for1(i, 1, m) read(y[i]);

	for1(i, 1, k) { int u=getint(), v=getint(); vis[u][v]=1; }

	for1(i, 1, n) for1(j, 1, m) if(!vis[i][j]) add(X(i), Y(j), 1);



	int S=t+1, T=S+1;

	for1(i, 1, n) in[X(i)]+=x[i], in[s]-=x[i];

	for1(i, 1, m) in[t]+=y[i], in[Y(i)]-=y[i];

	for1(i, 0, T) if(in[i]>0) add(S, i, in[i]); else if(in[i]<0) add(i, T, -in[i]);



	isap(S, T, T);

	add(t, s, oo);

	int ans=isap(S, T, T);

	rdm(S, i) if(e[i].cap) { puts("JIONG!"); return 0; }

	print(ans);

	return 0;

}

  

 


 

 

Description

有一个M * N的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第i行至少放置了Li个士兵, 第j列至少放置了Cj个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。

Input

第一行两个数M, N, K分别表示棋盘的行数,列数以及障碍的个数。 第二行有M个数表示Li。 第三行有N个数表示Ci。 接下来有K行,每行两个数X, Y表示(X, Y)这个格子是障碍。

Output

输出一个数表示最少需要使用的士兵个数。如果无论放置多少个士兵都没有办法占领整个棋盘,输出”JIONG!” (不含引号)

Sample Input

4 4 4
1 1 1 1
0 1 0 3
1 4
2 2
3 3
4 3

Sample Output

4
数据范围
M, N <= 100, 0 <= K <= M * N

HINT

 

Source

你可能感兴趣的:(网络流)