P2766 最长不下降子序列问题

16 篇文章 0 订阅

题目链接


题解:
1.最长不下降子序列,直接n^2 dp求出即可
2.最多可取出多少个长度为s的不下降子序列,根据dp数组建边跑最大流,具体建边:超级源点与dp[i]=1的点相连,dp[i]=s的点与终点相连,将每个点拆分成两个点,保证每个下标的值都只会被用一次就OK了
3.x1和xn可以多次使用,最多可取出多少不同的长度为s的不下降子序列,只需要将x1的点与源点相连的边和与自己出点相连的边改成inf,xn改一下和汇点的权值就可以表示多次使用了


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
const int MAXN = 1000+50;
const int MAXM = 2e6+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int n,m,s,t,res,ans,tot=1,head[MAXN],to[MAXM],w[MAXM],nxt[MAXM],h[MAXN];
int dp[MAXN],a[MAXN];
inline void ade(int u,int v,int ww){
    to[++tot]=v; w[tot]=ww; nxt[tot]=head[u]; head[u]=tot;
}
inline void add(int u,int v,int w){ ade(u,v,w); ade(v,u,w); }
inline int bfs(){
    queue<int> que; que.push(s); memset(h,0,sizeof(h)); h[s]=1;
    while(!que.empty()){
        int u=que.front(); que.pop();
        for(int i=head[u];i;i=nxt[i]){
            if(w[i] && !h[to[i]]){
                h[to[i]]=h[u]+1; que.push(to[i]);
            }
        }
    }
    return h[t];
}
inline int dfs(int x,int f){
    if(x==t) return f; int fl=0;
    for(int i=head[x];i&&f;i=nxt[i]){
        if(w[i] && h[to[i]]==h[x]+1){
            int mi=dfs(to[i],min(f,w[i]));
            w[i]-=mi; w[i^1]+=mi; fl+=mi; f-=mi;
        }
    }
    if(!fl) h[x]=-1;
    return fl;
}
inline void dinic(){
    while(bfs()) ans+=dfs(s,INF);
}
inline void solve1(){
    for(int i=1;i<=n;i++) dp[i]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(a[j]<=a[i])
                dp[i]=max(dp[i],dp[j]+1);
    for(int i=1;i<=n;i++) res=max(res,dp[i]);
    printf("%d\n",res);
}
inline void solve2(){
    for(int i=1;i<=n;i++) if(dp[i]==1) add(s,i,1);
    for(int i=1;i<=n;i++) if(dp[i]==res) add(i+n,t,1);
    for(int i=1;i<=n;i++) add(i,i+n,1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(a[j]<=a[i] && dp[i]==dp[j]+1)
                add(j+n,i,1);
    dinic(); printf("%d\n",ans);
}
inline void solve3(){
    add(s,1,INF); add(1,n+1,INF);
    if(dp[n]==res) add(n,n+n,INF),add(n+n,t,INF);
    dinic(); printf("%d\n",ans);
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    scanf("%d",&n); s=0,t=2*n+1;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    solve1();
    solve2();
    solve3();
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值