Leapin' Lizards dinic优化 拆点 最大流

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=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.

输入:

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.

输出:

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.

样例输入:

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..
........
........

样例输出:

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.

题意:

在一个房间里有一个网格,网格由很多点组成,一个点代表一个柱子,柱子有不同的高度,有很多只蜥蜴在这个网格范围里,现在这些蜥蜴要逃离这个网格,问最后剩下多个只蜥蜴,所以就跑最大流,算能逃出去多少只蜥蜴,用总数减去逃出去的。

建立超级源点和超级汇点,分为为0和2*ctn+1(ctn是网格的总个数),因为每根柱子跳出去一次高度就去减1,所以要对柱子拆点。

如果柱子i上有蜥蜴,从s到i建边(s,i,1)

如果柱子的高度为x,建边(i,i+ctn,x)

如果从柱子i可以直接逃出网格,建边(i+ctn,t,INF)

如果从柱子i不能直接逃出网格,还可以跳跃d的距离到别的柱子j上,建边(i+ctn,j,INF)

因为题目中说的d是单位元长度,所以这里的d是abs(行号差)+abs(列号差)

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=100000;
const int INF=0x7fffffff;
int cnt,s,t,ctn;
int head[maxn],cur[maxn],dis[maxn],net[maxn<<1];
char mapp[30][30],MAPP[30][30];
int num[30][30];
struct edge
{
    int u,v,w;
}node[maxn<<1];
void add(int u , int v , int w)
{
    node[cnt].u=u;
    node[cnt].v=v;
    node[cnt].w=w;
    net[cnt]=head[u];
    head[u]=cnt++;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    queue<int>q;
    dis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i!=-1;i=net[i])
        {
            edge e=node[i];
            if(!dis[e.v] && e.w > 0)
            {
                dis[e.v]=dis[e.u]+1;
                q.push(e.v);
                if(e.v==t)return 1;
            }
        }
    }
    return dis[t]!=0;
}
int dfs(int u, int mx)
{
    int res=0;
    if(u==t || mx==0) return mx;
    for(int &i=cur[u];i!=-1;i=net[i])
    {
        edge e = node[i];
        if( dis[e.v] == dis[e.u] + 1 && e.w>0)
        {
            int V=dfs(e.v,min(mx,e.w));
            node[i].w-=V;
            node[i^1].w+=V;
            mx-=V;
            res+=V;
            if(mx==0) break;
        }
    }
    return res;
}
int dinic()
{
    int maxflow=0;
    while (bfs())
    {
        memcpy(cur,head,sizeof(head));
        maxflow+=dfs(s,INF);
    }
    return maxflow;
}
int main()
{
    int T;
    scanf("%d",&T);
    int cas=1;
    while(T--)
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        ctn=0;
        int len;
        int SUM=0;//记录蜥蜴的个数
        int n,d;
        scanf("%d%d",&n,&d);
        for(int i=0;i<n;i++)
        {
            scanf("%s",mapp[i]);//因为每一行有多少柱子不确定,所以输入字符串,然后再转换
            len=strlen(mapp[i]);
            for(int j=0;j<len;j++)
            {
                MAPP[i][j]=mapp[i][j]-'0';//转换成数字
                num[i][j]=++ctn;//给每个柱子标点,顺便用ctn记录柱子的个数
            }
        }
        memset(mapp,0,sizeof(mapp));//因为上面用过了mapp,下面还要用,所以初始化一下
        for(int i=0;i<n;i++)
        {
            scanf("%s",mapp[i]);//这个图是有蜥蜴的图
        }
        s=0;t=ctn*2+1;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<len;j++)
            {
                if(MAPP[i][j])//高度必须大于0才能跳上去
                {
                    if(i<d || j<d || n<=i+d || len<=j+d)//如果能跳出去,那么与汇点建边
                    {
                        add(num[i][j]+ctn,t,INF);
                        add(t,num[i][j]+ctn,0);
                    }
                    add(num[i][j],num[i][j]+ctn,MAPP[i][j]);//拆点
                    add(num[i][j]+ctn,num[i][j],0);
                    for(int k=0;k<n;k++)
                        for(int l=0;l<len;l++)
                    {
                        int res=abs(i-k)+abs(j-l);//如果不能直接跳出去,那么可以跳到小于等于d距离的柱子上,建边
                        if(res>d)continue;
                        add(num[i][j]+ctn,num[k][l],INF);
                        add(num[k][l],num[i][j]+ctn,0);
                    }
                }
                if(mapp[i][j]=='L')//如果这个地方有蜥蜴,那么与源点连接起来
                {
                    add(s,num[i][j],1);
                    add(num[i][j],s,0);
                    SUM++;
                }
            }
        }
        int ans=SUM-dinic();//剩下的蜥蜴数
        if(!ans)//这个地方有坑,no和1的时候都是was,大于1的不管是偶数还是素数都用were
            printf("Case #%d: no lizard was left behind.\n",cas++);
        else if(ans==1)
            printf("Case #%d: 1 lizard was left behind.\n",cas++);
        else
            printf("Case #%d: %d lizards were left behind.\n",cas++,ans);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值