【网络流24题】最长递增子序列(DP+最大流)

本文介绍了一种结合动态规划与网络流算法解决最长不降子序列问题的方法。首先利用动态规划找出最长不降子序列的长度,然后通过构建网络流模型计算满足条件的子序列数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

    最长递增子序列
    题意:计算最长不降子序列长度s与数量,以及多次使用首末元素之后最多取出多少长度为s的最长不降子序列。

I think

    第一问DP求出f[i],表示以第i位为首位的最长不降子序列长度len,二三问网络流求解。增设源汇点S T,S向f[i]==len的点i连边,f[i]==1的点向T连边,满足i< j&&a[i]< =a[j]&&f[i]==f[j]+1的点对 < i,j > 连边,边的容量均为1,,跑最大流即为答案。第三问将S向1,N向T的连边容量更改为Inf,再次跑网络流得解。

Code

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int sm = 500+5;
const int sn = 125750;
const int Inf = 0x3f3f3f3f;

int x,y,Flw;
int N,M,S,T,tot=1,len;
int a[sm],f[sm],lev[sm],cur[sm];
int to[sn],hd[sm],c[sn],nxt[sn],_c[sn];

int Max(int x,int y) { return x>y?x:y; }
int Min(int x,int y) { return x<y?x:y; }

void Add(int u,int v,int w) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=_c[tot]=w;
    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=_c[tot]=0;
}
bool Bfs() {
    for(int i=1;i<=T;++i) lev[i]=0;
    int t; queue<int>q;
    q.push(S),lev[S]=1;
    while(!q.empty()) {
        t=q.front(),q.pop();
        for(int i=hd[t];i;i=nxt[i]) 
            if(c[i]>0&&!lev[to[i]]) {
                lev[to[i]]=lev[t]+1;
                if(to[i]==T) return 1;
                q.push(to[i]);
            }
    }
    return 0;
}
int Dfs(int x,int mx) {
    if(x==T||!mx) return mx;
    int f;
    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
        cur[x]=i;
        if(c[i]&&lev[to[i]]==lev[x]+1)
            if(f=Dfs(to[i],Min(mx,c[i])))
                return c[i]-=f,c[i^1]+=f,f;
    }
    return 0;
} 
void Dinic() {
    int f; Flw=0;
    while(Bfs()) {
        for(int i=1;i<=T;++i) cur[i]=0;
        while(f=Dfs(S,Inf)) Flw+=f;
    }
}
int main() {
    scanf("%d",&N);
    for(int i=1;i<=N;++i)
        scanf("%d",&a[i]);
    for(int i=N;i>=1;--i) {
        f[i]=1;
        for(int j=i+1;j<=N;++j)
            if(a[j]>=a[i]) 
                f[i]=Max(f[i],f[j]+1);
        len=Max(len,f[i]);
    }
    S=N+1,T=S+1;
    for(int i=1;i<=N;++i) {
        if(f[i]==len) {
            if(i==1) x=tot+1;
            Add(S,i,1);
        }
        if(f[i]==1) {
            if(i==N) y=tot+1;
            Add(i,T,1);
        }
    }
    for(int i=1;i<N;++i)
        for(int j=i+1;j<=N;++j)
            if(a[j]>=a[i]&&f[j]+1==f[i])    
                Add(i,j,1);

    Dinic();
    printf("%d\n%d\n",len,Flw);
    if(!x&&!y) printf("%d\n",Flw);
    else {
        memcpy(c,_c,sizeof(c));
        if(x) c[x]=Inf;
        if(y) c[y]=Inf;
        Dinic();
        printf("%d\n",Flw);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值