zoukankan      html  css  js  c++  java
  • 【bzoj2150】部落战争 有上下界最小流

    题目描述

    lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土。 A国是一个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们约定: 1. 每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。 2. 如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。 3. 每支军队都可以在任意一个城镇停止征战。 4. 所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走1*2的路线,而他们只能走R*C的路线。 lanzerb的野心使得他的目标是统一全国,但是兵力的限制使得他们在配备人手时力不从心。假设他们每支军队都能顺利占领这支军队经过的所有城镇,请你帮lanzerb算算至少要多少支军队才能完成统一全国的大业。

    输入

    第一行包含4个整数M、N、R、C,意义见问题描述。接下来M行每行一个长度为N的字符串。如果某个字符是'.',表示这个地方是城镇;如果这个字符时'x',表示这个地方是高山深涧。

    输出

    输出一个整数,表示最少的军队个数。

    样例输入

    【样例输入一】
    3 3 1 2
    ...
    .x.
    ...
    【样例输入二】
    5 4 1 1
    ....
    ..x.
    ...x
    ....
    x...

    样例输出

    【样例输出一】
    4
    【样例输出二】
    5


    题解

    很容易看出来的有上下界最小流

    将每个点拆成两个,设为xi和yi。S(代码中的b)->xi,容量为1,yi->T,容量为1,xi->yi,容量下界为1,上界为1。

    若点i能够到达点j,则yi->xj,容量为1。

    这个图的最小流即为答案。

    再具体一些的最小流转化为最大流的方法:

    S->xi,容量为1,yi->T,容量为1;若点i能够到达点j,则yi->xj,容量为1;

    建立超级源汇SS和TT(代码中的s和t),SS->yi,容量为1,xi->TT,容量为1;

    T->S,容量为inf。

    从SS到TT跑最大流,记录T->S的边的流量(即反向边的残量)为ans1。

    然后删除所有与SS或TT相连的边,删除T->S的边,从T到S跑最大流,记为ans2。

    ans1-ans2即为答案。

    注意往下走是行数增加而不是列数增加,不要弄混。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define N 6000
    #define M 200000
    #define inf 0x7fffffff
    #define pos(i , j , k) k * n * m + (i - 1) * m + j
    using namespace std;
    queue<int> q;
    int map[60][60] , head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N];
    char str[60];
    void add(int x , int y , int z)
    {
    	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
    }
    bool bfs()
    {
    	int x , i;
    	memset(dis , 0 , sizeof(dis));
    	while(!q.empty()) q.pop();
    	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 , r , c , i , j , b , e , ans = 0;
    	scanf("%d%d%d%d" , &n , &m , &r , &c) , b = 0 , e = 2 * n * m + 1 , s = 2 * n * m + 2 , t = 2 * n * m + 3;
    	add(e , b , inf);
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		scanf("%s" , str + 1);
    		for(j = 1 ; j <= m ; j ++ ) map[i][j] = (str[j] == '.');
    	}
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		for(j = 1 ; j <= m ; j ++ )
    		{
    			if(map[i][j])
    			{
    				add(b , pos(i , j , 0) , 1) , add(pos(i , j , 1) , e , 1) , add(s , pos(i , j , 1) , 1) , add(pos(i , j , 0) , t , 1);
    				if(i + r <= n && j + c <= m && map[i + r][j + c]) add(pos(i , j , 1) , pos(i + r , j + c , 0) , 1);
    				if(i + r <= n && j - c >= 1 && map[i + r][j - c]) add(pos(i , j , 1) , pos(i + r , j - c , 0) , 1);
    				if(i + c <= n && j + r <= m && map[i + c][j + r]) add(pos(i , j , 1) , pos(i + c , j + r , 0) , 1);
    				if(i + c <= n && j - r >= 1 && map[i + c][j - r]) add(pos(i , j , 1) , pos(i + c , j - r , 0) , 1);
    			}
    		}
    	}
    	while(bfs()) dinic(s , inf);
    	ans = val[3];
    	for(i = head[s] ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
    	for(i = head[t] ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
    	val[2] = val[3] = 0 , s = e , t = b;
    	while(bfs()) ans -= dinic(s , inf);
    	printf("%d
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    DownloadManager
    Android(蓝牙)
    Android下集成Paypal支付
    java 网络(socket)
    java 泛型的几点备忘
    设计模式(模板方法)
    STM32F4_引领入门
    如何查找STM32开发资料
    Keil(MDK-ARM)使用教程(三)_在线调试
    Keil(MDK-ARM)使用教程(二)_菜单
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7016586.html
Copyright © 2011-2022 走看看