hdu 2732 最大流

参考https://blog.csdn.net/liujc_/article/details/51179046

题意:
在n行的地图中,(列数没给),一个人可以最长跳d的距离,每一个柱子有一个限定条件,只能从这里跳出去几个人。给了两张图,一张表示柱子的限定条件,一张给了现在哪些柱子上有人。问有几个人不能跳到图的外面。
思路:
最大流建图。将图的外面抽象为一个汇点T,图中本可以跳出去的点可以连一条边到T。对于可以跳到图中别的点的,就可以从这个点连一条边过去。比较直观的想法就是这些边的容量都设为这个柱子限定的次数,最后跑最大流就好了。但是如果这个柱子可以跳到旁边很多的点上,每一个边的容量都是这个限定次数的话,等于限定的次数被放大了。那么就考虑拆点,将每个点拆成点1,点2,1->2连一条边容量为限定的次数。对于能跳到别的边,就从2连一条边到外面的点,容量为INF。
拆点
1.将能够直接跳出去的点,拆出的第一个点连到t,容量为限定的次数。
2.不能直接跳出去的,将点1连到点2,容量为限定的次数。
3.对于能跳到别的点的,将这个点2连到别的点的点1,容量为INF。
4.对于柱子上有人的,从s连一条边到该点的点1,容量为1。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
#define inf 0x7fffffff
const int INF = 0x3f3f3f3f;
struct Edge{
    int v,w,nxt;
}g[100001];
int head[100001];
int cnt;

void addEdge(int u,int v,int w){
    g[cnt].v = v;
    g[cnt].w = w;
    g[cnt].nxt = head[u];
    head[u] = cnt;
    ++ cnt;
}

int n,m,x,y,z;
int ans,flow;
int dis[100001];
queue<int> q;
int S,T;

void init(){
    memset(head,-1,sizeof(head));
    memset(g,0,sizeof(g));
    cnt = 0;
    memset(dis,-1,sizeof(dis));
    while(!q.empty()) q.pop();
    ans = 0;
}

int bfs(){
    memset(dis,-1,sizeof(dis));
    while(!q.empty()) q.pop();
    dis[S] = 0;
    q.push(S);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=g[i].nxt){
            int v = g[i].v;
            if(dis[v]==-1 && g[i].w > 0){
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T]!=-1;
}

int dfs(int u,int exp){
    if(u==T) return exp;
    int flow=0,tmp= 0;
    for(int i=head[u];i!=-1;i=g[i].nxt){
        int v = g[i].v;
        if((dis[v] == (dis[u]+1)) && (g[i].w>0)){
            tmp = dfs(v,min(exp,g[i].w));
            if(!tmp) continue;

            exp -= tmp;
            flow += tmp;

            g[i].w -= tmp;
            g[i^1].w += tmp;

            if(!exp) break;
        }
    }
    return flow;
}
int distance(int x1,int x2,int y1,int y2)
{
    return abs(x1-x2) + abs(y1-y2);
}
char mp1[30][30],mp2[30][30];
int main(){
    int f,d,tmp,t,ca=1;
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%d%d",&n,&d);
        for(int i=0;i<n;i++) scanf("%s",mp1[i]);
        for(int i=0;i<n;i++) scanf("%s",mp2[i]);
        m=strlen(mp1[0]);
        S =0;T =2*n*m+1;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++)
            {
                if(mp1[i][j]-'0'>0)
                {
                    if(i+1<=d||j+1<=d||n-i<=d||m-j<=d)//跳出边界
                    {
                        addEdge(i*m+j+1,T,mp1[i][j]-'0');
                        addEdge(T,i*m+j+1,0);
                        continue;
                    }
                    else
                    {
                        addEdge(i*m+j+1,i*m+j+1+n*m,mp1[i][j]-'0');
                        addEdge(i*m+j+1+n*m,i*m+j+1,0);
                    }

                    for(int ii=-d;ii<=d;ii++)
                    {
                        for(int jj=-d;jj<=d;jj++)
                        {
                            int x=i+ii;
                            int y=j+jj;
                            if(x==i&&j==y) continue;
                            if(x<0||y<0||x>=n||y>=m||distance(i,x,j,y) > d || mp1[x][y]-'0' == 0) continue;
                            addEdge(i*m+j+1+n*m,x*m+y+1,INF);
                            addEdge(x*m+y+1,i*m+j+1+n*m,0);
                        }
                    }
                }
            }
        }

        int sum=0;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(mp2[i][j]=='L')
                {
                    addEdge(S,i*m+j+1,1);
                    addEdge(i*m+j+1,S,0);
                    sum++;
                }
            }
        }
        while(bfs()){
            ans += dfs(S,inf);
        }
        ans=sum-ans;
        printf("Case #%d: ",ca++);
        if(ans >1) printf("%d lizards were left behind.\n",ans);
        else if(ans ==1) printf("1 lizard was left behind.\n",ans);
        else if (ans == 0) printf("no lizard was left behind.\n");
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值