【網絡流24題】最長不下降子序列問題

Luogu 2766

題意

給定一個正整數序列x1,...,xn 。

(1)計算其最長不下降子序列的長度s。

(2)從序列中最多可不重複使用數字地取出多少個長度爲s的不下降子序列。

(3)如果允許在取出的序列中多次使用x1和xn,求解(2)

題解

第一問很經典,數據範圍一看,N^2地DP就完事兒了,做出DP數組還能幫助第二問的解答。

第二問中,每個點只能用一次,老規矩直接拆點。用DP做出以第i位開始,最長的不下降子序列長度是多少。

超級源點連上所有長度位S的點,所有長度爲i的點連上超級匯點。若DP[i]==DP[j]+1,且i<j,a[i]>=a[j],那麼i,j連邊。跑最大流即可

第三問,改下第二問中首尾兩個點的流量即可。

代碼

#include<bits/stdc++.h>
#define MAXN 1109
#define MAXM 4000000+1109
#define INF 0x3fffffff

using namespace std;

int n,m;
int a[MAXN];
int DP[MAXN];

int S,T,Long;
int dis[MAXN],dl[MAXN];
int Head[MAXN],Next[MAXM],To[MAXM],FB[MAXM],Flow[MAXM],Cnt=0;//MAXMÒª¿¼ÂÇ·´±ß 
int cur[MAXN];//µ±Ç°»¡ÓÅ»¯ 

int Ans=0;

void ADD(int x,int y,int z)
{
    Cnt++;Next[Cnt]=Head[x];Head[x]=Cnt;To[Cnt]=y;FB[Cnt]=Cnt+1,Flow[Cnt]=z;
    Cnt++;Next[Cnt]=Head[y];Head[y]=Cnt;To[Cnt]=x;FB[Cnt]=Cnt-1,Flow[Cnt]=0;
}
bool BFS(int Begin,int End)
{
    int t=0,w=1,x,X;
    memset(dis,0xff,sizeof(dis));
    dis[Begin]=0;dl[1]=Begin;
    do
    {
        x=dl[++t];
        for(int i=Head[x];i;i=Next[i])
        {
            X=To[i];
            if(Flow[i]<=0||dis[X]>=0) continue;
            dis[X]=dis[x]+1;dl[++w]=X;
        }
    }while(t<w);
    if(dis[End]>0) return true;
    else return false;
}
int Find(int x,int MFLOW,int y)
{
    if(x==y) return MFLOW;
    int X,h; 
    for(int i=cur[x];i;i=Next[i])
    {
        cur[x]=i;
        X=To[i];
        if(Flow[i]>0&&dis[x]+1==dis[X]&&(h=Find(X,min(MFLOW,Flow[i]),y)))
        {
            Flow[i]-=h;
            Flow[FB[i]]+=h;
            return h;
        }
    }
    return 0;
}
int Solve(int x,int y)
{
    int X,Liu=0;
    while(1)
    {
        if(!BFS(x,y)) break;
        for(int i=S;i<=T;i++)//³õʼ»¯È«Ìåcur 
            cur[i]=Head[i];
        while((X=Find(x,0x7fffffff,y)))
            Liu+=X;
    }
    return Liu;
}



int main()
{
    scanf("%d",&n);
    S=0;T=2*n+1;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),DP[i]=1;
    
    for(int i=n;i;i--)
        for(int j=i+1;j<=n;j++)
            if(a[i]<=a[j])
                DP[i]=max(DP[i],DP[j]+1);
    
    for(int i=1;i<=n;i++)
        Long=max(Long,DP[i]);
    printf("%d\n",Long);
    
    for(int i=1;i<=n;i++)
    {
        if(DP[i]==Long) ADD(S,i,1);
        ADD(i,i+n,1);
        if(DP[i]==1) ADD(i+n,T,1);
        for(int j=i+1;j<=n;j++)
            if(a[i]<=a[j]&&DP[i]==DP[j]+1)
                ADD(i+n,j,1);
    }
    Ans=Solve(S,T);
    printf("%d\n",Ans);
    
    if(DP[1]==Long) ADD(S,1,INF);
    ADD(1,1+n,INF);
    if(DP[n]==1) ADD(n+n,T,INF);
    ADD(n,n+n,INF);
    Ans+=Solve(S,T);
    printf("%d",Ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章