luogu P2766 最长不下降子序列问题(LIS + 最大流)

题目描述

给定正整数序列 x_1 \ldots, x_nx1​…,xn​。

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

令 a_1, a_2, \ldots, a_sa1​,a2​,…,as​ 为构造 SS 时所使用的下标,b_1, b_2, \ldots, b_sb1​,b2​,…,bs​ 为构造 TT 时所使用的下标。且 \forall i \in [1,s-1]∀i∈[1,s−1],都有 a_i < a_{i+1}ai​<ai+1​,b_i < b_{i+1}bi​<bi+1​。则 SS 和 TT 不同,当且仅当 \exists i \in [1,s]∃i∈[1,s],使得 a_i \neq b_iai​​=bi​。

输入格式

第一行有一个正整数 n,表示给定序列的长度。接下来的一行有 n 个正整数x1​,...,xn​。

输出格式

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

输入输出样例

输入 #1复制

4
3 6 2 5

输出 #1复制

2
2
3

说明/提示

1≤n≤500

思路:

1、第一问直接LIS,求得的最长不下降子序列的长度为ss

2、第二问求可取出的长度为 s 的不下降子序列个数,每个数最多使用1次,考虑拆点,1~n表示入点,n + 1~2 * n表示出点,0表示超级源点,2 * n + 1表示超级汇点,dinic跑网络流,如下建图:

(1)所有入点向出点连边,流量为1

(2)超级源点向dp[i] == 1的入点连边,流量为1

扫描二维码关注公众号,回复: 11923869 查看本文章

(3)dp[i] == ss 的出点向超级汇点连边,流量为1

3、第三问只增加了一个条件,第一个数和最后一个数可以使用多次,它只影响了图中超级源点到1、1的入点到1的出点、n的入点到n的出点、n的出点到超级汇点(在dp[n] == ss的前提下),所以在第二问建图的基础上再添加几条边就可以了

(有关板子问题:第三问如果不重新构图,即直接在原图上加边,再跑出的maxflow是新增的流量,要和第二问的maxflow加起来

当然也可以init一遍重新构图,毕竟n就500,但这样显得很憨)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1005;
const int M = 10005;    //边集二倍
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int head[N], tot, n, m, x, s, t;
int Q[N];
int dep[N], cur[N], sta[N]; ///数组cur记录点u之前循环到了哪一条边
struct Edge {
    int to, next, cap, flow;
}edge[M];

void init() {
    tot = 2;
    memset(head, -1, sizeof(head));
    memset(edge, 0, sizeof(edge));
}

void addedge(int u, int v, int w, int rw = 0) {
    edge[tot].to = v;
    edge[tot].cap = w;
    edge[tot].flow = 0;
    edge[tot].next = head[u];
    head[u] = tot++;

    edge[tot].to = u;
    edge[tot].cap = rw;
    edge[tot].flow = 0;
    edge[tot].next = head[v];
    head[v] = tot++;
}

bool bfs(int s, int t, int n) {
    int fron = 0, tail = 0;
    memset(dep, -1, sizeof(dep[0]) * (n + 1));
    dep[s] = 0;
    Q[tail++] = s;
    while(fron < tail) {
        int u = Q[fron++];
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow && dep[v] == -1) {
                dep[v] = dep[u] + 1;
                if(v == t) return true;
                Q[tail++] = v;
            }
        }
    }
    return false;
}

int dinic(int s, int t, int n) {
    int maxflow = 0;
    while(bfs(s, t, n)) {
        for(int i = 0; i <= n; ++i) cur[i] = head[i];
        int u = s, tail = 0;
        while(cur[s] != -1) {
            if(u == t) {
                int tp = inf;
                for(int i = tail - 1; i >= 0; --i)
                    tp = min(tp, edge[sta[i]].cap - edge[sta[i]].flow);
                maxflow += tp;
                for(int i = tail - 1; i >= 0; --i) {
                    edge[sta[i]].flow += tp;
                    edge[sta[i] ^ 1].flow -= tp;
                    if(edge[sta[i]].cap - edge[sta[i]].flow == 0)
                        tail = i;
                }
                u = edge[sta[tail] ^ 1].to;
            }
            else if(cur[u] != -1 && edge[cur[u]].cap > edge[cur[u]].flow && dep[u] + 1 == dep[edge[cur[u]].to]) {
                sta[tail++] = cur[u];
                u = edge[cur[u]].to;
            }
            else {
                while(u != s && cur[u] == -1)
                    u = edge[sta[--tail] ^ 1].to;
                cur[u] = edge[cur[u]].next;
            }
        }
    }
    return maxflow;
}

int a[N], dp[N];

int main() {
    scanf("%d", &n);
    s = 0, t = 2 * n + 1;
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    if(n == 1) {
        printf("1\n1\n1\n");
        return 0;
    }
    int ss = 0;
    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);
        }
        ss = max(ss, dp[i]);
    }
    printf("%d\n", ss);
    init();
    for(int i = 1; i <= n; ++i) {
        addedge(i, i + n, 1);
        if(dp[i] == 1) addedge(s, i, 1);
        if(dp[i] == ss) addedge(i + n, t, 1);
        for(int j = 1; j < i; ++j) {
            if(dp[j] + 1 == dp[i] && a[j] <= a[i])
                addedge(j + n, i, 1);
        }
    }
    int maxflow = dinic(s, t, t + 1);
    printf("%d\n", maxflow);
    addedge(1, n + 1, inf);
    addedge(n, 2 * n, inf);
    addedge(s, 1, inf);
    if(dp[n] == ss) addedge(2 * n, t, inf);
    maxflow += dinic(s, t, t + 1);    ///maxflow += 
    printf("%d\n", maxflow);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43871207/article/details/109067824
今日推荐