• [FJOI2020]世纪大逃亡 题解


    FJOI2020 D1T1


    题目大意

    给出一个由 $n$ 行 $m$ 列的点构成的网格,其中第 $1$ 行,第 $n$ 行,第 $1$ 列与第 $m$ 列为边界,给出 $s$ 个点,求这 $s$ 个点到边界的最小的路径长度之和,要求路径不能交叉。 $n*mleq20000$。

    思路分析

    可以看出这是个费用流模板题。根据实测,本题数据卡 EK 单路增广,只能拿到 $70$ 分,需要多路增广才能过。虽然多路增广理论复杂度相同,但是本题中每次增广只能增加 $1$ 的流量,多路增广的确可以提高一定的效率。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #define ano ((i-1)^1)+1
    using namespace std;
    const int N=1e6+100,INF=0x7f7f7f7f;
    int n,m,S,tot,s,t,f,ans;
    int head[N],ver[2*N],Next[2*N],edge[2*N],cost[2*N];
    int minf[N],pre[N],d[N];
    bool v[N];
    void add(int x,int y,int z,int c)
    {
        ver[++tot]=y,edge[tot]=z,cost[tot]=c,Next[tot]=head[x],head[x]=tot;
        ver[++tot]=x,edge[tot]=0,cost[tot]=-c,Next[tot]=head[y],head[y]=tot;
    }
    bool spfa()
    {
        for(int i=0;i<=t;i++)
            d[i]=INF,v[i]=0;
        queue<int> q;
        q.push(s);
        v[s]=1,d[s]=0,minf[s]=INF;
        while(q.size())
        {
            int x=q.front();q.pop();v[x]=0;
            for(int i=head[x];i;i=Next[i])
            {
                if(!edge[i])
                    continue;
                int y=ver[i];
                if(d[x]+cost[i]<d[y])
                {
                    d[y]=d[x]+cost[i];
                    minf[y]=min(minf[x],edge[i]);
                    pre[y]=i;
                    if(!v[y])
                    {
                        v[y]=1;
                        q.push(y);
                    }
                }
            }
        }
        return d[t]!=INF;
    }/*
    void update()
    {
        int x=t;
        while(x!=s)
        {
            int i=pre[x];
            edge[i]-=minf[t];
            edge[ano]+=minf[t];
            x=ver[ano];
        }
        f+=minf[t];
        ans+=d[t]*minf[t];
    }*///单路增广
    int dinic(int x,int flow)
    {
        if(x==t)
            return flow;
        v[x]=1;
        int rest=flow,use;
        for(int i=head[x];i && rest;i=Next[i])
        {
            int y=ver[i];
            if(v[y] || !edge[i] || d[y]!=d[x]+cost[i])
                continue;
            use=dinic(y,min(rest,edge[i]));
            if(!use)
                d[y]=0;
            edge[i]-=use,edge[ano]+=use,rest-=use;
            ans+=cost[i]*use;
        }
        return flow-rest;
    }
    int id(int x,int y,int z)
    {
        return (x-1)*m+y+z*n*m;
    }
    void clear()
    {
        t=2*n*m+1,ans=tot=f=0;
        for(int i=0;i<=t;i++)
            head[i]=pre[i]=minf[i]=0;
    }
    int main()
    {
        //freopen("covid.in", "r", stdin);
        //freopen("covid.out", "w", stdout);
        while(scanf("%d%d%d",&n,&m,&S)!=EOF)
        {
            clear();
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    add(id(i,j,0),id(i,j,1),1,0);
                    if(i!=1)
                        add(id(i,j,1),id(i-1,j,0),1,1);
                    if(i!=n)
                        add(id(i,j,1),id(i+1,j,0),1,1);
                    if(j!=1)
                        add(id(i,j,1),id(i,j-1,0),1,1);
                    if(j!=m)
                        add(id(i,j,1),id(i,j+1,0),1,1);
                    if(i==1 || i==n || j==1 || j==m)
                        add(id(i,j,1),t,1,0);
                }
            for(int i=1,x,y;i<=S;i++)
            {
                scanf("%d%d",&x,&y);
                add(s,id(x,y,0),1,0);
            }/*
            while(spfa())
                update();*///单路增广
            while(spfa())
            {
                int flow;
                do{
                    for(int i=0;i<=t;i++)
                        v[i]=0;
                    flow=dinic(s,INF);
                    f+=flow;
                }while(flow);
            }
            if(f==S)
                printf("%d
    ",ans);
            else
                puts("-1");
        }
        return 0;
    }
  • 相关阅读:
    [Oracle DBA学习笔记] STARTUP详解
    亦步亦趋完成在CentOS 6.4下安装Oracle 11gR2
    ‘程序员’与‘页面仔’
    Linux下建立Oracle服务及其开机自启动
    解析并验证IE6及之前版本的'!important’ BUG
    浅谈CSS选择器中的空格
    在CentOS安装CMake
    关于CentOS下RPM的一些实例
    CentOS配置ssh无密码登录的注意点
    CentOS下的账户管理
  • 原文地址:https://www.cnblogs.com/TEoS/p/13325015.html
Copyright © 2020-2023  润新知