【网络流】 ◆SCOI 2007◆ 蜥蜴

◆SCOI 2007◆

蜥蜴


□谈一谈感想□

寒假的时间过得很快啊!2周的强化训练就结束了……树形DP的代码还历历在目,网络流的难题又摆在了我面前(毕竟这是一道省选题)。今天就选一道题作为网络流I-sap算法的总结吧!


□题目□

题目描述

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

输入输出格式

输入格式:
输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。以下r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。以下r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。
输出格式:
输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。

输入输出样例
输入样例#1:
5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........
输出样例#1:
1

100%的数据满足:1<=r, c<=20, 1<=d<=3

copy from LuoGu P2472


□解析□

简单地说一下算法吧——也就是网络流中的计算最大流的算法“I-sap”(或者你也可以用EK算法,但是I-sap时间复杂度更低)。

1.I-sap算法

相比于EK算法来说,I-sap算法是一种优化( O(nm2)O(n2m) O ( n m 2 ) → O ( n 2 m ) )。
这里有一个奇特的Bug——有时候EK算法竟然比I-sap快(甚至EK能AC的题,I-sap会超时)!但是作者还是使用的I-sap算法……
下面附一下I-sap的版,方便结合代码解释:

/*
cap[i][j]:i、j两点的边的权值(若没有边,则为0);
d[i]:i点的标号;
vd[i]:标号为i的点的个数;
*/
int aug(int u,int tot)
{
    int sum=0,min_d=n-1,del;
    if(u==t) return tot;
    for(int v=0;v<n;v++)
        if(cap[u][v]>0)
        {
            if(d[u]==d[v]+1)
            {
                del=min(tot-sum,cap[u][v]);
                del=aug(v,del);
                cap[u][v]-=del;cap[v][u]+=del;
                sum+=del;
                if(d[s]>=n) return sum;
                if(sum==tot) break;
            }
            min_d=min(min_d,d[v]);
        }
    if(!sum)
    {
        if(vd[d[u]]--==1) d[s]=n;
        d[u]=min_d+1;
        vd[d[u]]++;
    }
    return sum;
}
int ISAP()
{
    int flw=0;
    memset(d,0,sizeof d);
    memset(vd,0,sizeof vd);
    vd[0]=n;
    while(d[s]<n)
        flw+=aug(s,INF);
    return flw;
}

对,这就是版……套上去就行了。

2.建图

网络流的最大流算法的建图其实本质上就是连边设权(权即为该边的容量)。网络流的图有一个源点(s)和一个汇点(t),一般的网络流图如下图:
这里写图片描述
这是一个有向图,如果是无向图就连双向边就可以了。
本题的大概思路就是:
以所有‘L’点为起点,因此需要把源点连到每一个L点,而最终的目的是跳出边界,所以要将所有能跳出边界的点连到汇点。但是还有一个限制条件是石柱的高度,其实就是每一个石柱最多能够经过的次数。所以我们还需要创建一条边——既然高度只限制单一一个元素,那就干脆设置在一个元素内,但是元素是没有容量的,这时候就需要用到点化边的方法——拆单点为虚实两点!
这里写图片描述
然后就会有一个套路——“实连虚,虚连实”。

3.最大流

I-sap算法就是求最大流的,不多说了,答案就是最大流,跑一遍就完了。


□代码□

(不要以为这次我没有用图片你们就可以copy!!!)

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF int(1e8)
int r,c,jmp,sto[27][27],s,t,n,n_point,Tab[27][27],N;
bool anm[1007][1007];
int cap[1007][1007],d[20007],vd[20007];
void addedge(int u,int v,int c){cap[u][v]+=c;}
int aug(int u,int tot)
{
    int sum=0,min_d=n-1,del;
    if(u==t) return tot;
    for(int v=0;v<n;v++)
        if(cap[u][v]>0)
        {
            if(d[u]==d[v]+1)
            {
                del=min(tot-sum,cap[u][v]);
                del=aug(v,del);
                cap[u][v]-=del;cap[v][u]+=del;
                sum+=del;
                if(d[s]>=n) return sum;
                if(sum==tot) break;
            }
            min_d=min(min_d,d[v]);
        }
    if(!sum)
    {
        if(vd[d[u]]--==1) d[s]=n;
        d[u]=min_d+1;
        vd[d[u]]++;
    }
    return sum;
}
int ISAP()
{
    int flw=0;
    memset(d,0,sizeof d);
    memset(vd,0,sizeof vd);
    vd[0]=n;
    while(d[s]<n)
        flw+=aug(s,INF);
    return flw;
}
int main()
{
    s=0;
    scanf("%d%d%d",&r,&c,&jmp);
    for(int i=1;i<=r;i++)
    {
        char str[22]={};scanf("%s",str+1);
        for(int j=1;j<=c;j++)
        {
            sto[i][j]=str[j]-'0';
            if(sto[i][j]) Tab[i][j]=++n_point;
        }
    }
    t=2*n_point+1;n=t+1;
    for(int i=1;i<=r;i++)
    {
        char str[22]={};scanf("%s",str+1);
        for(int j=1;j<=c;j++)
        {
            if(str[j]=='L')
                addedge(s,Tab[i][j],1),N++;
            if(sto[i][j])
                addedge(Tab[i][j],Tab[i][j]+n_point,sto[i][j]);
            if(sto[i][j] && (i-jmp<1 || i+jmp>r || j-jmp<1 || j+jmp>c))
                addedge(Tab[i][j]+n_point,t,INF);
        }
    }
    for(int x1=1;x1<=r;x1++)
        for(int y1=1;y1<=c;y1++)
            if(sto[x1][y1])
                for(int x2=1;x2<=r;x2++)
                    for(int y2=1;y2<=c;y2++)
                        if(sto[x2][y2] && (x1!=x2 || y1!=y2) && (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)<=jmp*jmp)
                            addedge(Tab[x1][y1]+n_point,Tab[x2][y2],INF),addedge(Tab[x2][y2]+n_point,Tab[x1][y1],INF);
    int res=N-ISAP();
    printf("%d\n",res);
    return 0;
}

The End

Thanks for reading!

-Lucky_Glass

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值