BZOJ4200: [Noi2015]小园丁与老司机 最小流

7 篇文章 0 订阅
3 篇文章 0 订阅

题意:平面上有N(N<=50000)个点,从原点出发,每次可以走上、左、右、左上45度、右上45度五种方向,只有碰到一个未走过的点才能停止,求:
1.最多走多少点
2.输出一种方案
3.对于所有最优方案,只保留上、左上、右上的边,求最少条数的路径将所有边都覆盖
同一y坐标的点不超过1000个
前两问DP一下即可,将所有点按y为一关键字x为二关键字排序,然后对于每一行的每个点,枚举是从这行之前哪个点转移来,然后再n^2同行转移。同行转移的方案记录着实蛋疼了许久,感觉情况太多,结果突然想到其实只可能是一种方式——若从a走到同行的b,则先向b的反方向走到头,再走回b。于是就可以搞了。
第三问,这数据规模直接卡死了线性规划的内存,只好去学最小流了。。。
对于有下界的网络流,直接把每条边下界补齐可能流量不平衡。观察每个点的入流和出流,若进入的下界流比出去的下界流大a,则说明出去的非下界流要比进来的非下界流大a,因此建立超级源向这个点连一条流量为a的边,意味着只要这条边满流,就可以将这个点出边的流量合计扩充a,从而在流量守恒的条件下满足下界限制。出去的下界流大于进入的下界流同理,向超级汇连边。
如果原图有源汇的话还要从汇到源连一条无穷边使其转换为无源汇的图,每个点都满足流量守恒。
这样跑一遍从超级源到超级汇的最大流就是可行环流,也是从汇到源连的那条边的流量。若超级源连出的边不满流则无解。
最大流:删去超级源、超级汇所连的边以及从汇到源的边,在残量网络上再跑最大流,与第一次的可行流相加即为最大流。
最小流:先不连从汇到源的边,跑完一次后再连上那条边跑一次,这条边上流量就是最小流。实际上第一次的作用相当于尽可能把流量导到那条边以外的边上,类似于匹配问题把优先匹配的边先连上进行匹配,再连其他边(例如挑战NPC)
带费用的目前还没见过,那道叫支线剧情的被线性规划秒了。。。
然后这题倒着DP一下,将所有满足f[i]+g[j]==ans的(i,j)连下界为1的边,按上面方法跑一下最小流,光荣TLE。
于是这题显然一定有可行解,回顾最小流的过程,第二次流完后的流量就是连出去边的总流量,这个我们是知道的,于是只需要跑第一次,用满流减去第一次,就是最小流了。这就可以过了。
为啥ISAP暴力跑最小流就能过啊
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ext/pb_ds/assoc_container.hpp>
#define gm 50010
int n;
struct pnt
{
    int x,y;
    void get()
    {
        scanf("%d%d",&x,&y);
    }
    bool operator< (const pnt &b) const
    {
        return (y!=b.y)?(y<b.y):(x<b.x);
    }
}p[gm],o[gm];
int no[gm];
typedef __gnu_pbds::gp_hash_table<int,int> map;
int f[gm],g[gm],h[gm];
int ans=0,tail=0;
map ln,zs,ys;
int from[gm],__from[gm];
void print_path(int a)
{
    if(!a) return;
    int b=from[a];
    if(p[a].y!=p[b].y) print_path(b);
    else
    {
        print_path(__from[b]);
        if(p[a].x<p[b].x)
        {
            for(int i=b;p[i].y==p[b].y;++i)
            printf("%d ",no[i]);
            for(int i=b-1;p[i].x>p[a].x;--i)
            printf("%d ",no[i]);
        }
        else
        {
            for(int i=b;p[i].y==p[b].y;--i)
            printf("%d ",no[i]);
            for(int i=b+1;p[i].x<p[a].x;++i)
            printf("%d ",no[i]);
        }
    }
    printf("%d%c",no[a],a==tail?'\n':' ');
}
int s,t,ss,tt;
const int inf=0x7f7f7f7f;
struct e
{
    int t;
    e *n;
    int flow;
    e *r;
    e(int t,e *n,int flow):t(t),n(n),flow(flow){}
}*r[gm];
bool ingr[gm];
int rd[gm];
void link(int a,int b,int flow)
{
    r[a]=new e(b,r[a],flow);
    r[b]=new e(a,r[b],0);
    r[a]->r=r[b];
    r[b]->r=r[a];
}
void adde(int a,int b)
{
    ingr[a]=ingr[b]=1;
    ++rd[b],--rd[a];
    link(a,b,inf);
}
int d[gm];
bool bfs()
{
    memset(d,-1,n+5<<2);
    static int q[gm];
    int l=0,r=0;
    d[q[++r]=ss]=0;
    while(l!=r&&d[tt]==-1)
    {
        int x=q[++l];
        for(e *i=::r[x];i;i=i->n)
        if(i->flow&&d[i->t]==-1)
        d[q[++r]=i->t]=d[x]+1;
    }
    return d[tt]!=-1;
}
int send(int x=ss,int flow=inf)
{
    if(x==tt) return flow;
    int last=flow;
    for(e *i=r[x];i&&last;i=i->n)
    if(i->flow&&d[i->t]==d[x]+1)
    {
        int kre=send(i->t,i->flow<last?i->flow:last);
        last-=kre;
        i->flow-=kre;
        i->r->flow+=kre;
    }
    if(last) d[x]=-1;
    return flow-last;
}
int minflow()
{
    int ans=0;
    for(int i=0;i<=n;++i)
    if(ingr[i])
    {
        link(s,i,inf);
        link(i,t,inf);
        if(rd[i]>0)
        {
            link(ss,i,rd[i]);
            ans+=rd[i];
        }
        else if(rd[i]<0)
        {
            link(i,tt,-rd[i]);
        }
    }
    while(bfs()) ans-=send();
    return ans;
}
int main()
{
    scanf("%d",&n);
    s=n+1,t=n+2,ss=n+3,tt=n+4;
    for(int i=1;i<=n;++i)
    {
        p[i].get();o[i]=p[i];
    }
    std::sort(p+1,p+n+1);
    for(int i=1;i<=n;++i)
    no[std::lower_bound(p+1,p+n+1,o[i])-p]=i;
    ln[0]=zs[0]=ys[0]=0;
    for(int i=1,j=1;i<=n;i=j)
    {
        while(j<=n&&p[i].y==p[j].y) ++j;
        for(int k=i;k<j;++k)
        {
            f[k]=-1;
            map::point_iterator it;
            pnt &a=p[k];
            it=ln.find(a.x);
            if(it!=ln.end())
            {
                int q=it->second;
                if(f[q]!=-1&&f[q]+1>f[k])
                {
                    f[k]=f[q]+1;
                    from[k]=q;
                    if(f[k]>ans) ans=f[k],tail=k;
                }
            }
            it=zs.find(a.x+a.y);
            if(it!=zs.end())
            {
                int q=it->second;
                if(f[q]!=-1&&f[q]+1>f[k])
                {
                    f[k]=f[q]+1;
                    from[k]=q;
                    if(f[k]>ans) ans=f[k],tail=k;
                }
            }
            it=ys.find(a.x-a.y);
            if(it!=ys.end())
            {
                int q=it->second;
                if(f[q]!=-1&&f[q]+1>f[k])
                {
                    f[k]=f[q]+1;
                    from[k]=q;
                    if(f[k]>ans) ans=f[k],tail=k;
                }
            }
            h[k]=f[k];
            __from[k]=from[k];
        }
        for(int k=i;k<j;++k)
        {
            for(int l=i;l<k;++l)
            if(h[l]!=-1&&h[l]+(k-i)>f[k])
            {
                f[k]=h[l]+(k-i);
                from[k]=l;
                if(f[k]>ans) ans=f[k],tail=k;
            }
            for(int l=k+1;l<j;++l)
            if(h[l]!=-1&&h[l]+(j-k-1)>f[k])
            {
                f[k]=h[l]+(j-k-1);
                from[k]=l;
                if(f[k]>ans) ans=f[k],tail=k;
            }
            ln[p[k].x]=zs[p[k].x+p[k].y]=ys[p[k].x-p[k].y]=k;
        }
    }
    printf("%d\n",ans);
    print_path(tail);
    ln.clear();zs.clear();ys.clear();
    for(int i=n,j=n;i>=0;i=j)
    {
        while(j>=0&&p[i].y==p[j].y) --j;
        for(int k=i;k>j;--k)
        {
            if(f[k]==-1) continue;
            g[k]=1;
            map::point_iterator it;
            pnt &a=p[k];
            it=ln.find(a.x);
            if(it!=ln.end())
            {
                int q=it->second;
                if(g[q]+1>g[k])
                {
                    g[k]=g[q]+1;
                }
                if(g[q]+f[k]==ans)
                {
                    adde(k,q);
                }
            }
            it=zs.find(a.x+a.y);
            if(it!=zs.end())
            {
                int q=it->second;
                if(g[q]+1>g[k])
                {
                    g[k]=g[q]+1;
                }
                if(g[q]+f[k]==ans)
                {
                    adde(k,q);
                }
            }
            it=ys.find(a.x-a.y);
            if(it!=ys.end())
            {
                int q=it->second;
                if(g[q]+1>g[k])
                {
                    g[k]=g[q]+1;
                }
                if(g[q]+f[k]==ans)
                {
                    adde(k,q);
                }
            }
            h[k]=g[k];
        }
        for(int k=i;k>j;--k)
        {
            if(f[k]==-1) continue;
            for(int l=i;l>k;--l)
            {
                if(h[l]+(l-j-1)>g[k])
                {
                    g[k]=h[l]+(l-j-1);
                }
            }
            for(int l=k-1;l>j;--l)
            {
                if(h[l]+(i-l)>g[k])
                {
                    g[k]=h[l]+(i-l);
                }
            }
            ln[p[k].x]=zs[p[k].x+p[k].y]=ys[p[k].x-p[k].y]=k;
        }
    }
    printf("%d\n",minflow());
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值