HDU2732 Leapin' Lizards(最大流+拆点)

题意:

给两个n*m大小的图,第一个图上都是数字(0-3),每个数字代表一开时该坐标(i, j)上的柱子在第几次跳跃之后倒塌,0代表该位置上没有柱子,蜥蜴不能走没有柱子的地方;

第二个图由两种符号'L'和'.'组成,'L'代表此处有一个lizard(蜥蜴),'.'代表没有蜥蜴。在同一时刻, 每个点上只能有一个蜥蜴给蜥蜴能跳的最大曼哈顿距离d,

跳到矩阵的之外,(注意是整个矩阵的外边)即可以逃出去,当一只蜥蜴所处的位置能在跳跃范围d内跳出去的话就一定会跳出去。

求最少有几只蜥蜴没跳出去。

思路:

拆点。每个点坐标表示为(i, j)第i行第j列,那么用i*m+j表示节点序号,那么把该节点拆成两个点,一个是跳入点i*m+j,一个是跳出点i*m+j+n*m,

跳入点和跳出点不断交替连接,直到可以一步跳出边界时,让该点的跳出点与超级汇点t = n*m*2+1相连,边权为n*m,因为最多有m个蜥蜴走这个点;

超级源点s = 0 与一开始是'L'的点相连,边权为1,因为一个点只有一只蜥蜴。

然后跑最大流,判断一开始的蜥蜴数sum和最终maxflow的关系,对应输出即可。

(注意下输出格式啊,was, were, lizard, lizards单复数)

#include <bits/stdc++.h>
using namespace std;

const int maxn = 20 + 5;
const int maxm = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int n, m, d, T, s, t, maxflow;
int head[maxn*maxn*2], tot, dis[maxn*maxn*2];
char mp1[maxn][maxn], mp2[maxn][maxn];
int num[maxn][maxn], sum;
struct edge{
    int to, w, next;
} ed[maxm];

inline void add( int u, int v, int w ){
    ed[++tot].to = v; ed[tot].w = w; ed[tot].next = head[u]; head[u] = tot;
    ed[++tot].to = u; ed[tot].w = 0; ed[tot].next = head[v]; head[v] = tot;
}

inline int Abs( int a ){
    return a>=0 ? a:-a;
}

inline bool bfs(){
    memset( dis, 0, sizeof(dis) );
    queue<int> q;
    dis[s] = 1;
    q.push(s);
    while( !q.empty() ){
        int x = q.front();
        q.pop();
        for( int i=head[x]; i!=-1; i=ed[i].next ){
            int y = ed[i].to;
            if( ed[i].w && !dis[y] ){
                dis[y] = dis[x] + 1;
                q.push(y);
                if( y==t ) return 1;
            }
        }
    }
    return 0;
}

inline int min( int a, int b ){
    return a<b ? a:b;
}

inline int dfs( int x, int flow ){
    if( x==t ) return flow;
    int res = flow, k;
    for( int i=head[x]; i!=-1 && res; i=ed[i].next ){
        int y = ed[i].to;
        if( ed[i].w && dis[y]==dis[x]+1 ){
            k = dfs( y, min(res, ed[i].w) );
            if( !k ) dis[y] = 0;
            ed[i].w -= k;
            ed[i^1].w += k;
            res -= k;
        }
    }
    return flow - res;
}

inline void dinic(){
    int flow = maxflow = 0;
    while( bfs() )
        while( flow=dfs(s, inf) ) maxflow += flow;
}

int main(){
    // freopen("in.txt", "r", stdin);
    scanf("%d", &T);
    for( int k=1; k<=T; k++ ){
        tot = 1; sum = 0;
        memset( head ,-1, sizeof(head) );
        scanf("%d %d", &n, &d);
        for( int i=1; i<=n; i++ )
            scanf("%s", mp1[i]+1);
        m = strlen(mp1[1]+1);
        s = 0, t = n*m*2+1;
        for( int i=1; i<=n; i++ ){
            scanf("%s", mp2[i]+1);
           for( int j=1; j<=m; j++ ){
                num[i][j] = (i-1)*m+j;      //计算点(i, j)对应的序号
                if( mp2[i][j]=='L' ){
                    add( s, num[i][j], 1 );     //一开始有蜥蜴的点,让s与他们连边
                    sum ++;
                }
            }
        }
        for( int i=1; i<=n; i++ )
            for( int j=1; j<=m; j++ ){
                if( mp1[i][j]=='0' ) continue;      //没有柱子,不处理
                add( num[i][j], num[i][j]+n*m, mp1[i][j]-'0' );
                if( n-i+1<=d || m-j+1<=d || j<=d || i<=d ) add( num[i][j]+n*m, t, n*m );    //该点可直接出去,与汇点连边
                else{
                    for( int r=1; r<=n; r++ )
                        for( int c=1; c<=m; c++ ){
                            if( mp1[r][c]=='0' ) continue ; //没有柱子,不处理
                            if( r==i && c==j ) continue ;
                            if( Abs(r-i)+Abs(c-j)<=d ) add( num[i][j]+n*m, num[r][c], n*m ); //符合跳跃距离,跳出点与跳入点连边
                        }
                }
            }
        dinic();
        printf("Case #%d: ", k);
        if( maxflow==sum ) puts("no lizard was left behind.");
        else if( sum-maxflow==1 ) puts("1 lizard was left behind.");
        else printf("%d lizards were left behind.
", sum-maxflow);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/WAautomaton/p/10982774.html