【codevs1906】最长递增子序列问题 最大流

题目描述 Description

给定正整数序列x1,….. , xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。

输入描述 Input Description

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

输出描述 Output Description

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

样例输入 Sample Input

4
3 6 2 5

样例输出 Sample Output

2
2
3

数据范围及提示 Data Size & Hint

第一问DP,然后我就不会做了…

联系到最大流每个流代表一个方案,可以这样建图:

数列每个点拆点,然后连流量为1的边,表示只能选一遍。
对于第一问得到的dp数组,表示以第 i 位结尾的最长递增子串的长度。若 dp[i]=1 ,则s向i连边,容量为1;若 dp[i]=maxlen (也就是第一问答案),则 i e 连边,容量为1。
然后若 i>ja[i]>=a[j]dp[i]==dp[j]+1 ,则 i j 连边,容量为1。

然后跑最大流,每个流表示一个方案。

对于第三问,把与第1个和第n个点相关的边改为INF即可(也就是 <1,1><n,n><s,1><n,e> ),跑一遍最大流即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;

const int INF = 1000000010;
const int SZ = 1000010;

int head[SZ],nxt[SZ],tot = 1,n;

struct edge{
    int t,d;
}l[SZ];

void build(int f,int t,int d)
{
    l[++ tot].t = t;
    l[tot].d = d;
    nxt[tot] = head[f];
    head[f] = tot;
}

void insert(int f,int t,int d)
{
    build(f,t,d); build(t,f,0);
}

int deep[SZ];
queue<int> q;

bool bfs(int s,int e)
{
    memset(deep,0,sizeof(deep));
    deep[s] = 1;
    while(q.size()) q.pop();
    q.push(s);
    while(q.size())
    {
        int u = q.front(); q.pop();
        for(int i = head[u];i;i = nxt[i])
        {
            int v = l[i].t;
            if(!deep[v] && l[i].d)
            {
                deep[v] = deep[u] + 1;
                q.push(v);
                if(v == e) return true;
            }
        }
    }
    return false;
}

int dfs(int u,int flow,int e)
{
    if(u == e || flow == 0) return flow;
    int rest = flow;
    for(int i = head[u];i;i = nxt[i])
    {
        int v = l[i].t;
        if(deep[v] == deep[u] + 1 && l[i].d)
        {
            int f = dfs(v,min(rest,l[i].d),e);
            if(f > 0)
            {
                l[i].d -= f;
                l[i ^ 1].d += f;
                rest -= f;
                if(rest == 0) break;
            }
            else deep[v] = 0;
        }
    }
    return flow - rest;
}


int dinic(int s,int e)
{
    int ans = 0;
    while(bfs(s,e)) ans += dfs(s,INF,e);
    return ans;
}

int dp[SZ],num[SZ],len = 0;

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

int ask(int p)
{
    init();
    int s = n * 2 + 1;
    int e = n * 2 + 2;
    for(int i = 1;i <= n;i ++)
    {
        if(i == 1 || i == n)
            insert(i,i + n,p);
        else
            insert(i,i + n,1);
        if(dp[i] == 1)
        {
            if(i == 1)
                insert(s,i + n,p);
            else
                insert(s,i + n,1);
        }
        if(dp[i] == len)
        {
            if(i == n)
                insert(i + n,e,p);
            else
                insert(i + n,e,1);          
        }
    }
    for(int i = 1;i <= n;i ++)
        for(int j = i + 1;j <= n;j ++)
            if(num[i] <= num[j] && dp[i] + 1 == dp[j])
                insert(i + n,j,1);
    return dinic(s,e);
}

int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++)
        scanf("%d",&num[i]);
    for(int i = 1;i <= n;i ++)
    {
        dp[i] = 1;
        for(int j = 1;j < i;j ++)
            if(num[i] >= num[j])
                dp[i] = max(dp[i],dp[j] + 1);
        len = max(len,dp[i]);
    }
    printf("%d\n",len);
    printf("%d\n",ask(1));
    printf("%d\n",ask(INF));
    return 0;
}

你可能感兴趣的:(【codevs1906】最长递增子序列问题 最大流)