【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)

【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)

题面

BZOJ权限题,洛谷链接

题解

一道二合一的题目
考虑第一问。
先考虑如何计算六个方向上的第一个点。
左右上很好考虑,只需要按照\(x\)或者\(y\)轴排序就行了。
对于\(45\)度的斜角,两点一定在同一条直线上。
这条直线是\(x+y=b\)\(x-y=b\)
所以按照\(x+y\)\(x-y\)的值分类考虑,再按照顺序在\(x\)轴扫一遍就可以找到了。
考虑如何计算第一问的答案,我们发现\(y\)轴是单调不降的。
所以可以以\(y\)来划分截断,按照\(y\)轴从上往下进行转移。
\(f[i]\)表示\(i\)的答案,对于所有\(y\)值相同的点放在一起解决。
每次转移的时候,先将当前\(y\)相同的所有点通过左上右上和上三个方向进行转移。
然后再考虑左右的转移。因为左右转移是可以先向一个方向绕过去再走回来的,
所以考虑转移的时候需要额外注意一下。
所以再诶外设一个\(g\)\(f[i]\)表示从\(i\)出发不在同层内转移的最优答案。
\(g[i]\)表示从\(i\)出发,先转移到同层的某个点的最优答案。

第二问
先把所有可以存在于一个最优方案下的点连边。
因为每条边都至少被覆盖一次,发现这是一个上下界网络流问题。
因为保证有解,直接解决就好了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<vector>
using namespace std;
#define ll long long
#define RG register
#define MAX 50505
#define inf 1e6
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n;
struct Node{int x,y,id;}p[MAX];
bool cmpx(Node a,Node b){if(a.x!=b.x)return a.x<b.x;return a.y<b.y;}
bool cmpy(Node a,Node b){if(a.y!=b.y)return a.y<b.y;return a.x<b.x;}
bool cmpn(Node a,Node b){return a.id<b.id;}
int nt[MAX][5];
map<int,int> M;
int f[MAX],g1[MAX],g2[MAX],g[MAX],zy[MAX],a[MAX];
namespace Task1
{
    void Getdirt()
    {
        sort(&p[1],&p[n+1],cmpx);
        for(int i=1;i<n;++i)if(p[i].x==p[i+1].x)nt[p[i].id][2]=p[i+1].id;
        for(int i=1;i<=n;++i)if(p[i].x==0){nt[0][2]=p[i].id;break;}//up
        M.clear();
        for(int i=1;i<=n;++i)//left-up
        {
            int s=p[i].x+p[i].y;
            if(M[s])nt[p[i].id][3]=M[s];
            M[s]=p[i].id;
            if(s==0)nt[0][3]=p[i].id;
        }
        M.clear();
        for(int i=n;i>=1;--i)//right-up
        {
            int s=p[i].y-p[i].x;
            if(M[s])nt[p[i].id][4]=M[s];
            M[s]=p[i].id;
            if(s==0)nt[0][4]=p[i].id;
        }
    }
    int st[MAX],top;
    void Output(int x,int ans)
    {
        if(!ans)return;
        if(f[x]==ans){printf("%d ",x);Output(zy[x],ans-1);return;}
        int pos=a[x];while(pos>1&&p[pos-1].y==p[a[x]].y)--pos;
        top=0;while(p[pos].y==p[a[x]].y&&pos<=n)st[++top]=p[pos++].id;
        for(int i=1;i<=top;++i)if(st[i]==x){pos=i;break;}
        for(int i=1;i<pos;++i)
            if(f[st[i]]+top-i==g[x])
            {
                for(int j=pos;j<=top;++j)printf("%d ",st[j]);
                for(int j=pos-1;j>i;--j)printf("%d ",st[j]);
                Output(st[i],f[st[i]]);
                return;
            }
        for(int i=top;i>pos;--i)
            if(f[st[i]]+i-1==g[x])
            {
                for(int j=pos;j>=1;--j)printf("%d ",st[j]);
                for(int j=pos+1;j<i;++j)printf("%d ",st[j]);
                Output(st[i],f[st[i]]);
                return;
            }
    }
    void Solve()
    {
        Getdirt();sort(&p[1],&p[n+1],cmpy);
        for(int i=1;i<=n;++i)a[p[i].id]=i;
        for(int i=n,pos;i;i=pos)
        {
            top=0;pos=i;
            while(pos&&p[pos].y==p[i].y)st[++top]=p[pos--].id;
            reverse(&st[1],&st[top+1]);
            for(int j=1;j<=top;++j)
                for(int k=2;k<=4;++k)
                {
                    int u=st[j];
                    if(f[u]<g[nt[u][k]]+1)
                        f[u]=g[nt[u][k]]+1,zy[u]=nt[u][k];
                }
            for(int j=1;j<=top;++j)g[st[j]]=f[st[j]];
            for(int j=2,nw=1;j<=top;++j)
            {
                g[st[j]]=max(g[st[j]],f[st[nw]]+top-nw);
                if(f[st[nw]]-nw<f[st[j]]-j)nw=j;
            }
            for(int j=top-1,nw=top;j>=1;--j)
            {
                g[st[j]]=max(g[st[j]],f[st[nw]]+nw-1);
                if(f[st[nw]]+nw<f[st[j]]+j)nw=j;
            }
        }
        for(int k=2;k<=4;++k)
            if(f[0]<=g[nt[0][k]])f[0]=g[nt[0][k]],zy[0]=nt[0][k];
        printf("%d\n",f[0]);
        Output(zy[0],f[0]);puts("");
    }
}
namespace Task2
{
    struct Line{int v,next,w;}e[MAX<<5];
    int h[MAX],cnt=2,M[MAX],SS,TT,S,T;
    inline void Add(int u,int v,int w)
    {
        e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
        e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
    }
    set<pair<int,int> > SSS;
    bool vis[MAX];
    void Link(int x,int ans,bool fl)
    {
        if(!ans)return;
        if(!fl)if(SSS.count(make_pair(x,ans)))return;
        if(!fl)SSS.insert(make_pair(x,ans));
        if(f[x]==ans&&!vis[x])
        {
            vis[x]=true;
            for(int k=2;k<=4;++k)
                if(f[x]==g[nt[x][k]]+1)
                {
                    Add(x,nt[x][k],inf);
                    M[x]-=1;M[nt[x][k]]+=1;
                    Link(nt[x][k],ans-1,1);
                }
        }
        if(!fl)return;
        vector<int> st;int top=0;st.clear();st.push_back(0);
        int pos=a[x];while(pos>1&&p[pos-1].y==p[a[x]].y)--pos;
        top=0;while(p[pos].y==p[a[x]].y&&pos<=n)st.push_back(p[pos++].id),++top;
        for(int i=1;i<=top;++i)if(st[i]==x){pos=i;break;}
        for(int i=1;i<pos;++i)if(f[st[i]]+top-i==g[x])Link(st[i],f[st[i]],0);
        for(int i=top;i>pos;--i)if(f[st[i]]+i-1==g[x])Link(st[i],f[st[i]],0);
    }
    int level[MAX],cur[MAX];
    bool bfs(int S,int T)
    {
        memset(level,0,sizeof(level));level[S]=1;
        queue<int> Q;Q.push(S);
        while(!Q.empty())
        {
            int u=Q.front();Q.pop();
            for(int i=h[u];i;i=e[i].next)
                if(e[i].w&&!level[e[i].v])
                    level[e[i].v]=level[u]+1,Q.push(e[i].v);
        }
        return level[T];
    }
    int dfs(int u,int T,int flow)
    {
        if(u==T||!flow)return flow;
        int ret=0;
        for(int &i=cur[u];i;i=e[i].next)
        {
            int v=e[i].v,d;
            if(e[i].w&&level[v]==level[u]+1)
            {
                d=dfs(v,T,min(flow,e[i].w));
                ret+=d;flow-=d;
                e[i].w-=d;e[i^1].w+=d;
                if(!flow)break;
            }
        }
        return ret;
    }
    int Dinic(int S,int T)
    {
        int ret=0;
        while(bfs(S,T))
        {
            memcpy(cur,h,sizeof(cur));
            ret+=dfs(S,T,inf);
        }
        return ret;
    }
    int ans,rb;
    void Solve()
    {
        S=n+1;T=n+2;SS=n+3;TT=n+4;
        f[0]+=1;g[0]=inf;Link(0,f[0],0);
        for(int i=0;i<=n;++i)Add(i,T,inf);
        for(int i=0;i<=n;++i)Add(S,i,inf);
        for(int i=0;i<=n;++i)
            if(M[i]>0)Add(SS,i,M[i]);
            else Add(i,TT,-M[i]);
        Add(T,S,inf);rb=cnt-1;
        Dinic(SS,TT);
        h[T]=e[h[T]].next;h[S]=e[h[S]].next;
        ans-=Dinic(T,S);ans+=e[rb].w;
        printf("%d\n",ans);
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(),p[i].id=i;
    Task1::Solve();Task2::Solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cjyyb/p/9288367.html