hdu2732——建图最大流+拆点

题目链接:https://cn.vjudge.net/problem/HDU-2732

Your platoon of wandering lizards has entered a strange room in the labyrinth you are exploring. As you are looking around for hidden treasures, one of the rookies steps on an innocent-looking stone and the room's floor suddenly disappears! Each lizard in your platoon is left standing on a fragile-looking pillar, and a fire begins to rage below... Leave no lizard behind! Get as many lizards as possible out of the room, and report the number of casualties.
The pillars in the room are aligned as a grid, with each pillar one unit away from the pillars to its east, west, north and south. Pillars at the edge of the grid are one unit away from the edge of the room (safety). Not all pillars necessarily have a lizard. A lizard is able to leap onto any unoccupied pillar that is within d units of his current one. A lizard standing on a pillar within leaping distance of the edge of the room may always leap to safety... but there's a catch: each pillar becomes weakened after each jump, and will soon collapse and no longer be usable by other lizards. Leaping onto a pillar does not cause it to weaken or collapse; only leaping off of it causes it to weaken and eventually collapse. Only one lizard may be on a pillar at any given time.

Input

The input file will begin with a line containing a single integer representing the number of test cases, which is at most 25. Each test case will begin with a line containing a single positive integer n representing the number of rows in the map, followed by a single non-negative integer d representing the maximum leaping distance for the lizards. Two maps will follow, each as a map of characters with one row per line. The first map will contain a digit (0-3) in each position representing the number of jumps the pillar in that position will sustain before collapsing (0 means there is no pillar there). The second map will follow, with an 'L' for every position where a lizard is on the pillar and a '.' for every empty pillar. There will never be a lizard on a position where there is no pillar.Each input map is guaranteed to be a rectangle of size n x m, where 1 ≤ n ≤ 20 and 1 ≤ m ≤ 20. The leaping distance is
always 1 ≤ d ≤ 3.

Output

For each input case, print a single line containing the number of lizards that could not escape. The format should follow the samples provided below.

Sample Input

4
3 1
1111
1111
1111
LLLL
LLLL
LLLL
3 2
00000
01110
00000
.....
.LLL.
.....
3 1
00000
01110
00000
.....
.LLL.
.....
5 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........

Sample Output

Case #1: 2 lizards were left behind.
Case #2: no lizard was left behind.
Case #3: 3 lizards were left behind.
Case #4: 1 lizard was left behind.

题目翻译:

你的游荡蜥蜴排已经进入一个奇怪的房间,在迷宫,你正在探索。当你四处寻找隐藏的宝藏时,一个新秀踩在一块看起来无辜的石头上,房间的地板突然消失了!你排里的每只蜥蜴都站在一个脆弱的柱子上,火开始在下面肆虐...别留下蜥蜴!把尽可能多的蜥蜴带出房间,并报告伤亡人数。‎
‎房间里的柱子是网格对齐的,每个柱子单元从柱子向东、西、北、南分开。网格边缘的柱子距离房间边缘(安全)一个单位。不是所有的柱子都一定有蜥蜴。蜥蜴能够跳到任何空置的柱子上,这个柱子是他目前的支柱的d单位。站在房间边缘跳跃距离内的柱子上的蜥蜴可能总是跳到安全的地方...但有一个陷阱:每根柱子在每次跳跃后都会变弱,很快就会崩溃,不再被其他蜥蜴使用。跳到柱子上不会使其减弱或坍塌;只有从它跳下,它导致削弱,并最终崩溃。在任何给定时间,只有一只蜥蜴在柱子上。‎

‎输入‎

‎输入文件将以一行开头,该行包含表示测试用例数(最多为 25 个)的整数。每个测试用例将以一行包含表示地图中的行数的正整数 n 开始,后跟一个表示蜥蜴最大跳跃距离的非负整数 d。接下来将遵循两个地图,每张地图都是每行一行的字符映射。第一个地图将在每个位置包含一个数字 (0-3),表示该位置的支柱在折叠之前将保持的跳跃数(0 表示没有柱线)。第二张地图将紧随其后,每个位置都有一个"L",每个空柱上都有一个"."。在没有柱子的位置上永远不会有蜥蜴。每个输入映射保证为大小为 n x m 的矩形,其中 1 = n = 20,1 = m = 20。跳跃距离始终为‎
‎1 = d = 3。‎

‎输出‎

‎对于每个输入大小写,打印一行,其中包含无法逃脱的蜥蜴数量。格式应遵循下面提供的示例。

 

这个题难在建图上,首先我们需要拆点。把是柱子的点拆成两个。

之后怎么建图呢?

总的思路是这样:s->拆的入点u1->拆的出点u2->t

源点和入点之间的容量为1,表示有一只蜥蜴

入点u1和对应平行的出点u2相连,容量为柱子的高度,表示蜥蜴的最多跳跃次数。

如果一个点可以跳到另一个点,那么该点和另一个点建边,容量为柱子的高度,表示最多有多少只蜥蜴可以跳到另一个点。

如果某个点u2能够跳出界,那么该点和汇点建一条边,容量为该点柱子的高度,表示最多有多少只蜥蜴可以从这跳出去。

具体的过程可以借鉴我的代码,我用的是SAP求最大流。

#include <bits/stdc++.h>
using namespace std;
const int INF = 2e9;
const long long LNF = 9e18;
const int mod = 1e9+7;
const int MAXM = 1e5+10;
const int MAXN = 1e3+10;

int G[MAXN][MAXN];
int gap[MAXN], dis[MAXN], pre[MAXN], cur[MAXN];
int flow[MAXN][MAXN];
int sap(int start, int end, int nodenum)
{
    memset(cur, 0, sizeof(cur));
    memset(dis, 0, sizeof(dis));
    memset(gap, 0, sizeof(gap));
    memset(flow, 0, sizeof(flow));
    int u = pre[start] = start, maxflow = 0, aug = INF;
    gap[0] = nodenum;

    while(dis[start]<nodenum)
    {
        loop:
        for(int v = cur[u]; v<nodenum; v++)
        if(G[u][v]-flow[u][v]>0 && dis[u] == dis[v]+1)
        {
            aug = min(aug, G[u][v]-flow[u][v]);
            pre[v] = u;
            u = cur[u] = v;
            if(v==end)
            {
                maxflow += aug;
                for(u = pre[u]; v!=start; v = u, u = pre[u])
                {
                    flow[u][v] += aug;
                    flow[v][u] -= aug;
                }
                aug = INF;
            }
            goto loop;
        }

        int mindis = nodenum-1;
        for(int v = 0; v<nodenum; v++)
            if(G[u][v]-flow[u][v]>0 && mindis>dis[v])
            {
                cur[u] = v;
                mindis = dis[v];
            }
        if((--gap[dis[u]])==0) break;
        gap[dis[u]=mindis+1]++;
        u = pre[u];
    }
    return maxflow;
}
int T,s,t,N,d,n,m;
bool judge(int i,int j){
	if(i>=0&&i<n&&j>=0&&j<m) return 1;
	else return 0;
}
char p[25][25], l[25][25];
int id[25][25], pnum, lnum;
int main(int argc, char** argv) {
	cin>>T;
	int kase=0;
	while(T--){
		scanf("%d%d",&n,&d);
		for(int i = 0;i<n;++i) scanf("%s",p[i]);
		for(int i = 0;i<n;++i) scanf("%s",l[i]);
		m = strlen(p[0]);
		pnum=lnum=0;
		for(int i = 0;i<n;++i){
			for(int j = 0;j<m;++j){
				if(l[i][j]=='L')	lnum++;//统计蜥蜴的个数 
				if(p[i][j]-'0')	id[i][j]=pnum++;//给每个柱子编号 
			}
		}
		s=2*pnum,t=2*pnum+1,N=2*pnum+2;//超级源点,超级汇点和点的总数 
		memset(G,0,sizeof(G));
		for(int i = 0;i<n;++i){
			for(int j = 0;j<m;++j){
				int num=p[i][j]-'0';
				if(num){
					if(l[i][j]=='L') G[s][id[i][j]]=1;//如果当前点有蜥蜴,那么源点和当前点建立一条边,容量为 1 ,表示有一只蜥蜴 
					G[id[i][j]][pnum+id[i][j]]=num;//拆点,入点和出点连边,容量为高度,限制蜥蜴的最大跳跃数 
					int flag=0;
				for(int xd=-d;xd<=d;xd++){//枚举坐标 
					for(int yd=abs(xd)-d;yd<=d-abs(xd);yd++){
							if(judge(i+xd,j+yd)&&(p[i+xd][j+yd]-'0'))//如果当前点蜥蜴可以蹦到另一个点
									//那么两点连一条边,容量为当前点的高度,表示最多能有多少只青蛙可以跳到另一个点 
								G[pnum+id[i][j]][id[i+xd][j+yd]]=num;
							if(!judge(i+xd,j+yd)) flag=1;
						}
					if(flag) G[pnum+id[i][j]][t]=num;//当前点如果能跳出界,那么和汇点连一条边,容量为当前点的高度,表示最多能有多少只蜥蜴跳出去。 
					}
				}
			} 
		}
		int left=lnum-sap(s,t,N);//求出最大流 
		if(left==0) printf("Case #%d: no lizard was left behind.\n",++kase);
		else if(left==1) printf("Case #%d: 1 lizard was left behind.\n",++kase);
		else printf("Case #%d: %d lizards were left behind.\n",++kase,left);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值