【bzoj1458】士兵占领 有上下界最小流

时间:2024-09-03 12:04:14

题目描述

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

输入

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

输出

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

样例输入

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

样例输出

4


题解

有源汇有上下界网络流最小流

s向每个代表第i行的点连一条下界为li,上界为inf的边。

每个代表第i列的点向t连一条下界为ci,上界为inf的边。

对于每个没有障碍的点,该点所在行向该点所在列连一条下界为0,上界为1的边。

这样把原题中的行列转化为网络流中的点,原图中的点转化为网络流中的边。

然后跑最小流即可。

求最小流步骤:

1.求可行流并判断是否满流,若不满流,则无解。

2.记录自己连的t->s的边的流量为ans1。

3.删除与SS或TT相连的边,删除自己连的t->s的边。

4.交换s和t,即以t为源点,s为汇点求最大流为ans2,得最小流为ans1-ans2。

#include <cstdio>
#include <cstring>
#include <queue>
#define inf 0x3fffffff
using namespace std;
queue<int> q;
int head[210] , to[30000] , val[30000] , next[30000] , cnt = 1 , in[210] , dis[210] , s , t;
bool map[110][110];
void add(int x , int y , int z)
{
to[++cnt] = y;
val[cnt] = z;
next[cnt] = head[x];
head[x] = cnt;
}
bool bfs()
{
int x , i;
while(!q.empty()) q.pop();
memset(dis , 0 , sizeof(dis));
dis[s] = 1 , q.push(s);
while(!q.empty())
{
x = q.front() , q.pop();
for(i = head[x] ; i ; i = next[i])
{
if(val[i] && !dis[to[i]])
{
dis[to[i]] = dis[x] + 1;
if(to[i] == t) return 1;
q.push(to[i]);
}
}
}
return 0;
}
int dinic(int x , int low)
{
if(x == t) return low;
int temp = low , i , k;
for(i = head[x] ; i ; i = next[i])
{
if(val[i] && dis[to[i]] == dis[x] + 1)
{
k = dinic(to[i] , min(temp , val[i]));
if(!k) dis[to[i]] = 0;
val[i] -= k , val[i ^ 1] += k;
if(!(temp -= k)) break;
}
}
return low - temp;
}
int main()
{
int n , m , k , i , j , x , y , tempcnt , sum = 0 , ans;
scanf("%d%d%d" , &n , &m , &k);
s = 0 , t = n + m + 1;
for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &x) , add(s , i , inf) , add(i , s , 0) , in[s] -= x , in[i] += x;
for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &x) , add(i + n , t , inf) , add(t , i + n , 0) , in[i + n] -= x , in[t] += x;
while(k -- ) scanf("%d%d" , &x , &y) , map[x][y] = 1;
for(i = 1 ; i <= n ; i ++ )
for(j = 1 ; j <= m ; j ++ )
if(!map[i][j])
add(i , j + n , 1) , add(j + n , i , 0);
add(t , s , inf) , tempcnt = cnt , add(s , t , 0);
s = n + m + 2 , t = n + m + 3;
for(i = 0 ; i <= n + m + 1 ; i ++ )
{
if(in[i] > 0) add(s , i , in[i]) , add(i , s , 0) , sum += in[i];
else add(i , t , -in[i]) , add(t , i , 0);
}
while(bfs()) sum -= dinic(s , inf);
if(sum)
{
printf("JIONG!\n");
return 0;
}
ans = val[tempcnt ^ 1];
for(i = s ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
for(i = t ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
val[tempcnt] = val[tempcnt ^ 1] = 0;
s = n + m + 1 , t = 0;
while(bfs()) ans -= dinic(s , inf);
printf("%d\n" , ans);
return 0;
}