【bzoj1458】士兵占领 有上下界最小流
题目描述
有一个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; }