【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
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
数据范围
M, N <= 100, 0 <= K <= M * N
HINT
Source
博客地址:www.cnblogs.com/iwtwiioi 本文为博主原创文章,未经博主允许不得转载。一经发现,必将追究法律责任。