Luogu 2766 - 最長不降低子序列問題 - [LIS問題][DP+網絡流]

題目連接:https://www.luogu.org/problemnew/show/P2766c++

 

題解(大量參考https://blog.csdn.net/ZscDst/article/details/82423342):網絡

第一問,能夠用DP求解,用 $f[i]$ 表示以 $a[i]$ 爲結尾的最長不減子序列的長度,DP時間複雜度 $O(n^2)$,假設求得長度爲 $len$。spa

第二問咱們能夠用網絡流來求解:.net

一、因爲每一個點只能被選一次,因此拆點,控制每一個數只能選一次。rest

二、源點往全部 $f[i]=1$ 的點連一條邊權爲 $1$ 的邊,全部 $f[i]=len$ 的點往匯點連一條邊權爲 $1$ 的邊。code

三、若是 j 點是由 i 點轉移獲得的(即f[i]+1==f[j] && a[i] <= a[j]),那麼 i+n 往 j 連一條邊權爲1的邊。blog

第三問:ci

$x[1]$ 和 $x[n]$ 能夠使用屢次,首先 $x[1],x[n]$ 拆出來的兩個點之間的容量須要修改。get

同時,此題中必然有一條邊從源點連向 $x[1]$,因此這條邊的容量也要修改。$x[n]$ 不必定是匯點,若 $x[n]$ 是匯點,那麼 $x[n]$ 到匯點的邊的容量也要修改。it

若是從新構圖去跑可能會超時,咱們知道網絡流是能夠繼續在殘量網絡中跑的,因此咱們直接再添加新邊,再繼續跑網絡流,累加到第二問的答案上,即爲第三問的答案。

 

AC代碼:

#include<bits/stdc++.h>
#define I(x) x
#define O(x) (n+x)
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=505;

int n,x[maxn];
int f[maxn];

struct Edge{
    int u,v,c,f;
};
struct Dinic
{
    static const int SIZE=2*maxn;
    int s,t; //源點匯點
    vector<Edge> E;
    vector<int> G[SIZE];
    void init(int l,int r)
    {
        E.clear();
        for(int i=l;i<=r;i++) G[i].clear();
    }
    void addedge(int from,int to,int cap)
    {
        E.push_back((Edge){from,to,cap,0});
        E.push_back((Edge){to,from,0,0});
        G[from].push_back(E.size()-2);
        G[to].push_back(E.size()-1);
    }
    int dist[SIZE],vis[SIZE];
    queue<int> q;
    bool bfs() //在殘量網絡上構造分層圖
    {
        memset(vis,0,sizeof(vis));
        while(!q.empty()) q.pop();
        q.push(s);
        dist[s]=0;
        vis[s]=1;
        while(!q.empty())
        {
            int now=q.front(); q.pop();
            for(int i=0;i<G[now].size();i++)
            {
                Edge& e=E[G[now][i]]; int nxt=e.v;
                if(!vis[nxt] && e.c>e.f)
                {
                    dist[nxt]=dist[now]+1;
                    q.push(nxt);
                    vis[nxt]=1;
                }
            }
        }
        return vis[t];
    }
    int dfs(int now,int flow)
    {
        if(now==t || flow==0) return flow;
        int rest=flow,k;
        for(int i=0;rest>0 && i<G[now].size();i++)
        {
            Edge &e=E[G[now][i]]; int nxt=e.v;
            if(e.c>e.f && dist[nxt]==dist[now]+1)
            {
                k=dfs(nxt,min(rest,e.c-e.f));
                if(!k) dist[nxt]=0; //剪枝,去掉增廣完畢的點
                e.f+=k; E[G[now][i]^1].f-=k;
                rest-=k;
            }
        }
        return flow-rest;
    }
    int mf; //存儲最大流
    int maxflow()
    {
        mf=0;
        int flow=0;
        while(bfs()) while(flow=dfs(s,INF)) mf+=flow;
        return mf;
    }
}dinic;

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&x[i]);

    int len=1;
    dinic.init(dinic.s=0,dinic.t=2*n+1);
    for(int i=1;i<=n;i++)
    {
        dinic.addedge(I(i),O(i),1);

        f[i]=1;
        for(int j=1;j<i;j++)
            if(x[j]<=x[i]) f[i]=max(f[i],f[j]+1);
        len=max(len,f[i]);

        if(f[i]==1) dinic.addedge(dinic.s,I(i),1);
        else
        {
            for(int j=1;j<i;j++)
                if(x[j]<=x[i] && f[j]+1==f[i]) dinic.addedge(O(j),I(i),1);
        }
    }
    cout<<len<<endl;

    for(int i=1;i<=n;i++)
        if(f[i]==len) dinic.addedge(O(i),dinic.t,1);

    int ans=dinic.maxflow();
    cout<<ans<<endl;


    dinic.addedge(dinic.s,I(1),INF), dinic.addedge(I(1),O(1),INF);
    dinic.addedge(I(n),O(n),INF);
    if(f[n]==len) dinic.addedge(O(n),dinic.t,INF);

    cout<<ans+dinic.maxflow()<<endl;
}
相關文章
相關標籤/搜索