【P2766】 最长不下降子序列问题

题目描述

«问题描述:

给定正整数序列x1,...,xn 。

(1)计算其最长不下降子序列的长度s。

(2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。

(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。

«编程任务:

设计有效算法完成(1)(2)(3)提出的计算任务。

输入格式

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

输出格式

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

输入输出样例

输入 #1
4
3 6 2 5
输出 #1
2
2
3

说明/提示

n500

 


【解题思路】

  运用最小割解决问题的本质是

  找到多个问题同时存在且矛盾的情况,利用网络流找到最优的解

  于是这个问题的解决方式就能够这样解决

  首先,拆点,一个入点一个出点,考虑他们的连接过程

  因为源头流量为 1 ,于是就能够形成单一的链,保证情况独立,能够排除矛盾情况

  开头的是长度为 1 的,因为最长的能够包括所有子串,所以连接至汇点

  然后对每个符合要求,对这道题来说就是

  能够顺利组成增加一个字符串,使这个子串增长

  如果选择了这个子串之后,就能够或者这个子串相应的流量

  在最大流的性质和思想下最终也就能够得到最终答案了

  


【代码】

 

#include<cstdio>
#include<queue>
#include<cstring>
#define ll int 
using namespace std;
const int MAXN = 5010;
const int MAXM = 200010;
const ll  INF = (1ll << 31) - 1;
struct note
{
    int to;
    int nt;
    int rev;
    ll cal;
};
struct edge
{
    note arr[MAXM];
    int siz;
    int maxn;
    int a[MAXN];
    int dp[MAXN];
    int  st[MAXN];
    int  dis[MAXN];
    int  cur[MAXN];
    int  depth[MAXN];
    int  top;
    int n, m, s, t;
    edge()
    {
        memset(st, -1, sizeof(st));
        memset(depth, -1, sizeof(depth));
        memset(dis, -1, sizeof(dis));
        top = 0;
    }
    void read()
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
    }
    int LIS()
    {
        
        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);
                }
            }
        maxn = -1;
        for (int i = 1; i <= n; i++)
            if (maxn < dp[i])
                maxn = dp[i];
        return maxn;
    }
    void build()
    {
        t = 2*n+2;
        for (int i = 1; i <= n; i++)
        {
            add(i, i + n, 1);
            if(dp[i]==1)
                add(0, i, 1);
            if (dp[i] == maxn)
                add(i + n, t, 1);
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <i; j++)
                if (a[j] <= a[i] && dp[j] + 1 == dp[i])
                    add(j+n, i , 1);
        s = 0;
        siz = 2 * n+1;
    }
    bool dep()
    {
        queue<int> q;
        q.push(s);
        memset(depth, -1, sizeof(depth));
        depth[s] = 0;
        while (!q.empty())
        {
            int v = q.front(); q.pop();
            for (int i = st[v]; i != -1; i = arr[i].nt)
            {
                int to = arr[i].to;
                if (!arr[i].cal)
                    continue;
                if (depth[to] != -1)
                    continue;
                depth[to] = depth[v] + 1;
                q.push(to);
            }
        }
        return (depth[t] != -1);

    }
    void add(int x, int y, ll z)
    {
        top++; arr[top] = { y,st[x],top + 1,z }; st[x] = top;
        top++; arr[top] = { x,st[y],top - 1,0 }; st[y] = top;
    }
    ll dfs(int now, ll val)
    {
        if (now == t || !val)
            return val;
        ll flow = 0;
        for (int& i = cur[now]; i != -1; i = arr[i].nt)
        {
            int to = arr[i].to;
            if (depth[to] != depth[now] + 1)
                continue;
            ll f = dfs(to, min(arr[i].cal, val));
            if (!f || !arr[i].cal)
                continue;
            flow += f;
            arr[i].cal -= f;
            arr[arr[i].rev].cal += f;
            val -= f;
            if (!val)
                return flow;
        }
        return flow;
    }
    ll dinic()
    {
        ll flow = 0;
        ll f;
        while (dep())
        {
            for (int i = 0; i <= siz; i++)
                cur[i] = st[i];
            while (f = dfs(s, INF))
                flow += f;
        }
        return flow;
    }
};
edge road,tmp;
int main()
{
    road.read();
    //printf("**\n");
    printf("%d\n",road.LIS());
    //printf("**\n");
    //printf("\n");
    road.build();
    //printf("**\n");
    tmp = road;
    printf("%d\n", tmp.dinic());
    if(road.dp[1]==1)
    road.add(road.s, 1, INF);
    if(road.dp[road.n]==road.maxn)
    road.add(road.n * 2, road.t, INF);
    road.add(1, road.n + 1, INF);
    road.add(road.n, road.n * 2, INF);
    printf("%d", road.dinic());
    return 0;
}
View Code

 

posted @ 2019-08-08 21:09  rentu  阅读(191)  评论(0编辑  收藏  举报