BZOJ1458:士兵占领(有上下界最小流)

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

Solution

有源汇上下界最小流……按行和列建二分图,分别向源汇点建立上下界为$[L_i,INF]$和$[C_i,INF]$的边,然后没有障碍点的两两连$[0,1]$的边,跑一遍就完了。

一个上下界网络流讲的很好的博客。注意别忘了$s$和$t$也要与$ss$和$tt$连边!

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #define N (20009)
 6 #define INF (0x7f7f7f7f)
 7 using namespace std;
 8 
 9 struct Edge{int to,next,flow;}edge[N*50];
10 int n,m,k,vis[109][109];
11 int s=20001,t=20002,ss=20003,tt=20004;
12 int Depth[N],A[N];
13 int head[N],num_edge;
14 queue<int>q;
15 
16 inline int read()
17 {
18     int x=0,w=1; char c=getchar();
19     while (!isdigit(c)) {if (c=='-') w=-1; c=getchar();}
20     while (isdigit(c)) x=x*10+c-'0', c=getchar();
21     return x*w;
22 }
23 
24 void add(int u,int v,int l)
25 {
26     edge[++num_edge].to=v;
27     edge[num_edge].next=head[u];
28     edge[num_edge].flow=l;
29     head[u]=num_edge;
30 }
31 
32 void Add(int u,int v,int l,int r)
33 {
34     add(u,v,r-l); add(v,u,0);
35     A[u]-=l; A[v]+=l;
36 }
37 
38 int DFS(int x,int low,int t)
39 {
40     if (x==t || !low) return low;
41     int f=0;
42     for (int i=head[x]; i; i=edge[i].next)
43         if (Depth[edge[i].to]==Depth[x]+1)
44         {
45             int Min=DFS(edge[i].to,min(low,edge[i].flow),t);
46             edge[i].flow-=Min;
47             edge[((i-1)^1)+1].flow+=Min;
48             f+=Min; low-=Min;
49             if (!low) break;
50         }
51     if (!f) Depth[x]=-1;
52     return f;
53 }
54 
55 bool BFS(int s,int t)
56 {
57     memset(Depth,0,sizeof(Depth));
58     Depth[s]=1; q.push(s);
59     while (!q.empty())
60     {
61         int x=q.front(); q.pop();
62         for (int i=head[x]; i; i=edge[i].next)
63             if (!Depth[edge[i].to] && edge[i].flow)
64             {
65                 Depth[edge[i].to]=Depth[x]+1;
66                 q.push(edge[i].to);
67             }
68     }
69     return Depth[t];
70 }
71 
72 int Dinic(int s,int t)
73 {
74     int ans=0;
75     while (BFS(s,t)) ans+=DFS(s,INF,t);
76     return ans;
77 }
78 
79 int main()
80 {
81     n=read(); m=read(); k=read(); s=n+m+1; t=s+1;
82     for (int i=1; i<=n; ++i) Add(s,i,read(),INF);
83     for (int i=1; i<=m; ++i) Add(i+n,t,read(),INF);
84     for (int i=1; i<=k; ++i) vis[read()][read()]=1;
85     for (int i=1; i<=n; ++i)
86         for (int j=1; j<=m; ++j)
87             if (!vis[i][j]) Add(i,j+n,0,1);
88     int sum=0;
89     for (int i=1; i<=n+m+2; ++i)
90         if (A[i]>0) sum+=A[i], add(ss,i,A[i]), add(i,ss,0);
91         else add(i,tt,-A[i]), add(tt,i,0);
92     add(t,s,INF); add(s,t,0);
93     if (Dinic(ss,tt)!=sum) {puts("JIONG!"); return 0;}
94     for (int i=head[ss]; i; i=edge[i].next) edge[i].flow=edge[((i-1)^1)+1].flow=0;
95     for (int i=head[tt]; i; i=edge[i].next) edge[i].flow=edge[((i-1)^1)+1].flow=0;
96     int flow0=edge[num_edge].flow;
97     edge[num_edge].flow=edge[num_edge-1].flow=0;
98     printf("%d\n",flow0-Dinic(t,s));
99 }

转载于:https://www.cnblogs.com/refun/p/10448854.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值