网络流24题(六)

网络流24题(六)

六、最长不下降子序列

题目描述

给定正整数序列 \(x_1 \ldots, x_n​\)

  1. 计算其最长不下降子序列的长度 ss。
  2. 如果每个元素只允许使用一次,计算从给定的序列中最多可取出多少个长度为 ss 的不下降子序列。
  3. 如果允许在取出的序列中多次使用 \(x_1\)​ 和 \(x_n\)​(其他元素仍然只允许使用一次),则从给定序列中最多可取出多少个不同的长度为 \(s\) 的不下降子序列。

\(a_1, a_2, \ldots, a_s\)为构造 \(S\) 时所使用的下标,\(b_1, b_2, \ldots, b_s\)​ 为构造 \(T\) 时所使用的下标。且 \(\forall i \in [1,s-1]\),都有 \(a_i \lt a_{i+1},b_i \lt b_{i+1}\)。则 \(S\)\(T\) 不同,当且仅当 \(\exists i \in [1,s]\),使得 \(a_i \neq b_i\)

输入格式

第一行有一个正整数 \(n\),表示给定序列的长度。接下来的一行有 \(n\) 个正整数\(x_1, ..., x_n\)

输出格式

  • 第 1 行是最长不下降子序列的长度 \(s\)
  • 第 2 行是可取出的长度为 \(s\) 的不下降子序列个数。
  • 第 3 行是允许在取出的序列中多次使用 \(x_1\)​ 和 \(x_n\)​ 时可取出的长度为 \(s\)不同的不下降子序列个数。

题解

模型:

最大流,分层图,拆点

建图与实现:

对于问题一,\(dp\)即可,答案为\(len\)
对于问题二,就利用问题一得到的\(f[i]\)分层,当\(f[i] = f[j]+1\) && \(a[i] < a[j]\) && \(i < j\),连接一条有向边,容量为1。
这样一来,任意一条从\(f[1]\)的点到\(f[len]\)的路径都是满足的,跑最大流即可。但是注意有结点只可以使用一次的限制,可以理解为结点容量。在这种情况下,可以选择拆点,容量为1。
对于问题三,只需要处理一下第一个点和最后一个点就行,但是题目似乎有一个点是错的,只有一个点的情况应该是可以无线用的,我下下来特判一下。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long

const ll N = 1050,M = 2e5+5,inf = 0x3f3f3f3f;
ll head[N],cnt = 1;
struct Edge{ll to,w,nxt;}edge[M];

void add(ll u,ll v,ll w){
    edge[++cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}
ll n,m,s,t,lv[N],cur[N];
bool bfs(){
    memset(lv, -1, sizeof(lv));
    lv[s] = 0;
    memcpy(cur, head, sizeof(head));
    queue<int> q;q.push(s);
    while (!q.empty()){
        int p = q.front();q.pop();
        for (ll eg = head[p]; eg; eg = edge[eg].nxt){
            ll to = edge[eg].to, vol = edge[eg].w;
            if (vol > 0 && lv[to] == -1)lv[to] = lv[p] + 1, q.push(to);
        }
    }
    return lv[t] != -1;
}
ll dfs(ll p = s, ll flow = inf){
    if (p == t)return flow;
    int rmn = flow;
    for (ll &eg = cur[p]; eg; eg = edge[eg].nxt){
        if (!rmn)break;
        int to = edge[eg].to, vol = edge[eg].w;
        if (vol > 0 && lv[to] == lv[p] + 1){
            ll c = dfs(to, min(vol, rmn));
            rmn -= c;
            edge[eg].w -= c;
            edge[eg ^ 1].w += c;
        }
    }
    return flow - rmn;
}
ll dinic(){
    ll ans = 0;
    while(bfs()) ans += dfs();
    return ans;
}
ll a[N],f[N],len = -1;
void dp(){
    for(ll i = 1;i <= n;i++) {
        for (ll j = 1; j < i; j++) {
            if (a[i] >= a[j]) f[i] = max(f[i], f[j] + 1);
        }
        len = max(len, f[i]);
    }
    cout<<len<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n;
    for(ll i = 1;i <= n;i++)cin>>a[i],f[i] = 1;
    if(n == 1) {
        cout<<1<<endl<<1<<endl<<1<<endl;
        return 0;
    }
    dp();
    for(ll i = 1;i <= n;i++){
        for(ll j = i+1;j <= n;j++){
            if(f[j] == f[i]+1 && a[i] <= a[j]){
                add(i+n,j,1);
                add(j,i+n,0);
            }
        }
    }
    s = 0,t = 2*n+1;
    for(ll i = 1;i <= n;i++){
        if(f[i] == 1){
            add(s,i,1);
            add(i,s,0);
        }
        add(i,i+n,1);
        add(i+n,i,0);
        if(f[i] == len){
            add(i+n,t,1);
            add(t,i+n,0);
        }
    }
    cout<<dinic()<<endl;
    memset(head,0,sizeof head);
    cnt = 1;
    for(ll i = 1;i <= n;i++){
        for(ll j = i+1;j <= n;j++){
            if(f[j] == f[i]+1 && a[i] <= a[j]){
                add(i+n,j,1);
                add(j,i+n,0);
            }
        }
    }
    s = 0,t = 2*n+1;
    for(ll i = 1;i <= n;i++){
        if(f[i] == 1){
            add(s,i,1);
            add(i,s,0);
        }
        add(i,i+n,1);
        add(i+n,i,0);
        if(f[i] == len){
            add(i+n,t,1);
            add(t,i+n,0);
        }
    }
    if(f[1] == 1)add(s,1,n);add(1,s,0);
    add(1,1+n,n);add(1+n,1,0);
    add(n,2*n,n);add(2*n,n,0);
    if(f[n] == len)add(2*n,t,n);add(t,2*n,0);
    cout<<dinic()<<endl;
    return 0;
}
posted @ 2021-10-03 13:32  Paranoid5  阅读(31)  评论(0编辑  收藏  举报