HDU2732Leapin' Lizards(最大流SAP,建图---折点法)

Leapin' Lizards

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1531    Accepted Submission(s): 623


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

Source
 题意:给出n<=20,d,接下来给出两个图,每个图有n行,m列(m没有给出)。第一个图表示哪些位置有柱子,0表示没有柱子,非0则有,非0数(范围:0~3)代表从当前柱子可以跳的最多次数,当跳的次数等于当前位置的数时,柱子就消失了。人可以从一个柱子跳到另一个柱子,跳过的距离不能超过 d 。接下来一个图表示人所在的位置,L:表示有人,保证给出的有人位置有柱子。 问:图中有多少个人没有跳出界限。
解题:最大流SAP,关建是建图:折点法,把每个有柱子的本身点的位置看成一条边(点(i*m+j+1)--->点(i*m+j+1+ n*m)),边的容量为柱子可跳的次数,再从每一个有柱子的点(i,j)到可跳范围内有柱子的点( ti , tj )建一条边,容量可设为>=点(i,j)在图中的数子大小,为什么可以这样建:因为一个人从一个点跳到另一个点只会跳一次,所以一次即一人。 建立到汇点t=2*n*m+1的边,即从某个点可以跳出边界,那么这个点可以与t点建一条边,容量同理。把s=0点看成源点,那么从源到点有人的位置点建一条边,容量为1。所以整个图内从源点到汇点流的是人数(求的就是人数)。流出的最大量为可跳到边界的人数。
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
#define captype int

const int MAXN = 1010;   //点的总数
const int MAXM = 2000010;    //边的总数
const int INF = 1<<30;
struct EDG{
    int to,next;
    captype cap,flow;
} edg[MAXM];
int eid,head[MAXN];
int gap[MAXN];  //每种距离(或可认为是高度)点的个数
int dis[MAXN];  //每个点到终点eNode 的最短距离
int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边
int pre[MAXN];

void init(){
    eid=0;
    memset(head,-1,sizeof(head));
}
//有向边 三个参数,无向边4个参数
void addEdg(int u,int v,captype c,captype rc=0){
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
}

captype maxFlow_sap(int sNode,int eNode, int n){//n是包括源点和汇点的总点个数,这个一定要注意
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    pre[sNode] = -1;
    gap[0]=n;
    captype ans=0;  //最大流
    int u=sNode;
    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点
        if(u==eNode){   //找到一条可增流的路
            captype Min=INF ;
            int inser;
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min
            if(Min>edg[i].cap-edg[i].flow){
                Min=edg[i].cap-edg[i].flow;
                inser=i;
            }
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                edg[i].flow+=Min;
                edg[i^1].flow-=Min;  //可回流的边的流量
            }
            ans+=Min;
            u=edg[inser^1].to;
            continue;
        }
        bool flag = false;  //判断能否从u点出发可往相邻点流
        int v;
        for(int i=cur[u]; i!=-1; i=edg[i].next){
            v=edg[i].to;
            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){
                flag=true;
                cur[u]=pre[v]=i;
                break;
            }
        }
        if(flag){
            u=v;
            continue;
        }
        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1
        int Mind= n;
        for(int i=head[u]; i!=-1; i=edg[i].next)
        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){
            Mind=dis[edg[i].to];
            cur[u]=i;
        }
        gap[dis[u]]--;
        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径
                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流
        dis[u]=Mind+1;//如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1
        gap[dis[u]]++;
        if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边
    }
    return ans;
}
int abs(int a){return a>0?a:-a;}
int main(){
    int n,d;
    char g1[50][50],g2[50][50];
    int T,_cas=0;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&d);
        for(int i=0; i<n; i++)
            scanf("%s",g1[i]);
        for(int i=0; i<n; i++)
            scanf("%s",g2[i]);

        init();
        int m=strlen(g1[0]);
        
        //点本身作为一个边,点i*m+j+1入渡只来自外界,出度只有一个,也就是自身的另一个点 i*m+j+1+n*m, 点i*m+j+1+n*m 只有出度
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
            if(g1[i][j]!='0')
                addEdg(i*m+j+1 , i*m+j+1+n*m , g1[i][j]-'0');

        int s=0 , t=2*n*m+1; //源点,汇点
        
        for(int i=0; i<n; i++)//起始位置(i,j),可到达的位置(ti,tj)
        for(int j=0; j<m; j++)
        if(g1[i][j]!='0'){

            if(i+d>=n || i-d<0 || j+d>=m || j-d<0){ //可以出界,也就是到达汇点
                addEdg(i*m+j+1+n*m , t , g1[i][j]-'0'); continue;
            }
            
            for(int ti=i-d; ti<=i+d; ti++)
            if(ti>=0&&ti<n)
            for(int tj=j-d; tj<=j+d; tj++)
            if(tj>=0&&tj<m && (ti!=i || tj!=j)){
                if(abs(ti-i)+abs(tj-j)>d||g1[ti][tj]=='0')
                    continue;
                addEdg(i*m+j+1+n*m , ti*m+tj+1 , g1[i][j]-'0');//边的容量只要是>=g1[i][j]-'0'都可以
            }
        }

        int ans=0;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
            if(g2[i][j]=='L'){
                addEdg(s , i*m+j+1, 1) , ans++; 
            }

        ans -= maxFlow_sap(s , t , t+1);
        if(ans>1)
            printf("Case #%d: %d lizards were left behind.\n",++_cas , ans );
        else if(ans==1)
            printf("Case #%d: %d lizard was left behind.\n",++_cas , ans );
        else
            printf("Case #%d: no lizard was left behind.\n",++_cas );
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值