蜥蜴【网络流】

题目大意:

在一个 r c 列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。
每行每列中相邻石柱的距离为 1 ,蜥蜴的跳跃距离是 d ,即蜥蜴可以跳到平面距离不超过 d 的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减 1 (如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为 1 ,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。


思路:

很明显的最大流问题。为了满足一个石柱最多跳 a [ i ] [ j ] 只蜥蜴,就要将每个石柱进行拆点,中间流量为 a [ i ] [ j ] ,这样就保证了最多只有 a [ i ] [ j ] 只蜥蜴可以跳这个石柱。那么这个建图就很明显了:

  1. 将源点 S 向每个有蜥蜴的柱子连边,边权为1。
  2. 将与边界距离不足 d 的柱子与汇点 T 连边,边权为 I N F
  3. 将拆好的柱子两两连边,边权为 a [ i ] [ j ]
  4. 将直线距离不大于 d 的柱子连边,边权为 I N F

跑一边最大流即可得到能逃出的蜥蜴数量,再用总蜥蜴数量减去得到的答案即可。


代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define Inf 0x7f
#define INF 1e9
using namespace std;

int n,m,d,a[41][41],head[500001],dep[500001],s,t,k,ans,num,sum,x,y;
char c;

struct edge
{
    int next,to,c;
}e[500001];

void add(int from,int to,int c)
{
    k++;
    e[k].c=c;
    e[k].to=to;
    e[k].next=head[from];
    head[from]=k;
} 

bool check(int x1,int y1,int x2,int y2)
{
    return ((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)<=d*d);
    //计算两个柱子的直线距离
    //两边同时开方得 sqrt((x1-x2)^2+(y1-y2)^2)<=d
    //再利用毕达哥拉斯定理计算
}

bool bfs()  //分层
{
    queue<int> q;
    q.push(s);
    memset(dep,Inf,sizeof(dep));
    dep[s]=0;
    while (q.size())
    {
        int u=q.front();
        q.pop();
        for (int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if (dep[v]>dep[u]+1&&e[i].c)  
            {
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    } 
    return (dep[t]<Inf);
}

int dfs(int u,int low)
{
    int lows=0;
    if (u==t) return low;
    for (int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if (dep[v]==dep[u]+1&&e[i].c)
        {
            lows=dfs(v,min(low,e[i].c));
            if (!lows) continue;
            e[i].c-=lows;  //正向边
            e[(i+1)^1-1].c+=lows;  //反向边
            return lows;
        }
    }
    return 0;
}

int main()
{
    scanf("%d%d%d",&n,&m,&d);
    s=0;
    t=n*m*2+1;
     //柱子a编号为1~n*m,柱子b编号为n*m+1~2*n*m
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
     {
        scanf("%1d",&a[i][j]);
        x=(i-1)*m+j;  //计算编号
        add(x,x+n*m,a[i][j]);
        add(x+n*m,x,0);  //柱子a和柱子b连接
        if (i-d<1||i+d>n||j-d<1||j+d>m)  //能跳到边界外
        {
            add(x+n*m,t,INF);
            add(t,x+n*m,0);  //与t相连
        }
     }
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
     {
        cin>>c;
        if (c=='L')  //蜥蜴
        {
            num++;
            x=(i-1)*m+j;
            add(s,x,1);
            add(x,s,0);  //与s相连
        }
     }
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
      for (int k=1;k<=n;k++)
       for (int l=1;l<=m;l++)
        if (check(i,j,k,l))  //距离不大于d
        {
            x=(i-1)*m+j;
            y=(k-1)*m+l;
            add(x+n*m,y,INF);
            add(y,x+n*m,0);
            add(y+n*m,x,INF);
            add(x,y+n*m,0);
        }
    while (bfs())
    {
        while (sum=dfs(s,INF))
         ans+=sum;
    }
    printf("%d\n",num-ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/81478741