洛谷 P2766 最长不下降子序列问题

\(\quad\) 一旦知道这道题是网络流,思路就很好想了。
\(\quad\) 先用 \(O(n^2)\)dp 求出 \(dp\)\(s\)。在这里 \(dp_i\) 表示在 \([1,i]\) 范围中最后一个元素为 \(i\) 的最长不下降子序列长度。

for(int i = 1; i <= n; ++i)
    for(int j = 0; j < i; ++j){
        if(x[j] <= x[i])
            dp[i] = max(dp[i], dp[j] + 1);
        s = max(s, dp[i]);
    }

\(\quad\) \(\mathrm {I.}\)
\(\qquad\) 直接输出 \(s\) 即可。
\(\quad\) \(\mathrm {II.}\)
\(\qquad\) \(\forall i,j\) 满足 \(i > j\)\(x_i \geq x_j\),连一条从 \(r_j\)\(l_i\),容量为 \(1\) 的有向边;\(\forall i\) 满足 \(dp_i = 1\),连一条从源点到 \(l_i\),容量为 \(1\) 的有向边;\(\forall i\) 满足 \(dp_i = s\),连一条从 \(r_i\) 到汇点,容量为 \(1\) 的有向边,\(\forall i\),连一条从 \(l_i\)\(r_i\),容量为 \(1\) 的有向边。这个图的最大流即为答案。
\(\quad\) \(\mathrm {III.}\)
\(\qquad\) 开始把这道题看成所有元素都可以多次使用了,调了好久~直接连 \((s,l_1)\)\((l_1, r_1)\)\((l_n,r_n)\)\((r_n,t)\) 容量为 \(+\infty\) 的有向边即可。

#include <cstdio>
#include <cstring>
#include <queue>

inline int max(const int& a, const int& b){
    return a > b ? a : b;
}

inline int min(const int& a, const int& b){
    return a < b ? a : b;
}

const int MAXN = 5e2 + 19, INF = 0x3f3f3f3f;

struct Edge{
    int to, next, c;
}edge[MAXN * MAXN << 1];

int cnt = -1, head[MAXN << 1];

inline void add(int from, int to, int c){
    edge[++cnt].to = to;
    edge[cnt].c = c;
    edge[cnt].next = head[from];
    head[from] = cnt;
}

int n, x[MAXN], s;
int dep[MAXN << 1];

int bfs(void){
    std::memset(dep, 0, sizeof(dep)); dep[0] = 1;
    std::queue<int>q; q.push(0);
    while(!q.empty()){
        int node = q.front();
        for(int i = head[node]; i != -1; i = edge[i].next)
            if(!dep[edge[i].to] && edge[i].c)
                dep[edge[i].to] = dep[node] + 1, q.push(edge[i].to);
        q.pop();
    }
    return dep[n + n + 1];
}

int dfs(int node, int flow){
    if(node == n + n + 1 || !flow)
        return flow;
    int stream = 0, f;
    for(int i = head[node]; i != -1; i = edge[i].next)
        if(dep[edge[i].to] == dep[node] + 1 && (f = dfs(edge[i].to, min(flow, edge[i].c)))){
            flow -= f, stream += f;
            edge[i].c -= f, edge[i ^ 1].c += f;
            if(!flow)
                break;
        }
    return stream;
}

int dinic(void){
    int flow = 0;
    while(bfs())
        flow += dfs(0, INF);
    return flow;
}

int dp[MAXN], ans;

int main(){
    std::scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        std::scanf("%d", x + i);
    for(int i = 1; i <= n; ++i)
        for(int j = 0; j < i; ++j){
            if(x[j] <= x[i])
                dp[i] = max(dp[i], dp[j] + 1);
            s = max(s, dp[i]);
        }
    std::printf("%d\n", s);
    std::memset(head, -1, sizeof(head));
    for(int i = 1; i <= n; ++i)
        add(i, i + n, 1), add(i + n, i, 0);
    for(int i = 1; i <= n; ++i){
        if(dp[i] == 1)
            add(0, i, 1), add(i, 0, 0);     
        for(int j = 1; j < i; ++j)
            if(dp[i] == dp[j] + 1 && x[i] >= x[j])
                add(j + n, i, 1), add(i, j + n, 0);
        if(dp[i] == s)
            add(i + n, n + n + 1, 1), add(n + n + 1, i + n, 0);
    }
    std::printf("%d\n", ans = dinic());
    add(0, 1, INF), add(1, 0, 0), add(1, 1 + n, INF), add(1 + n, 1, 0);
    if(dp[n] == s)
        add(n, n + n, INF), add(n + n, n, 0), add(n + n, n + n + 1, INF), add(n + n + 1, n + n, 0);
    std::printf("%d\n", ans + dinic());
    return 0;
}

\(\quad\) 这道题洛谷上有很多奇奇怪怪的数据,好烦啊。

猜你喜欢

转载自www.cnblogs.com/natsuka/p/12300474.html