zoukankan      html  css  js  c++  java
  • 【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

    样例输出


    题解

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

    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!
    ");
    		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
    " , ans);
    	return 0;
    }

     

     

  • 相关阅读:
    构建之法 团队和流程
    构建之法之个人技术和流程重点介绍
    几种数据库连接
    Ckidt
    hibernate_04_hibernate多对多的关系映射
    hibernate_03_hibernate一对多的关系映射
    hibernate_02_hibernate的入门
    hibernate_01_SSH环境搭建
    SpringBoot_05_ssm拦截器和默认欢迎页面的设置
    SSM14-通过AOP实现日志记录
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6550547.html
Copyright © 2011-2022 走看看