hdu 3998 Sequence(DP+最大流,求最多的不相交路径)

本文探讨了如何使用动态规划和最大流算法解决最长上升子序列问题,并通过实例详细介绍了求解过程和应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1149    Accepted Submission(s): 416


Problem Description
There is a sequence X (i.e. x[1], x[2], ..., x[n]). We define increasing subsequence of X 
as x[i1], x[i2],...,x[ik], which satisfies follow conditions:
1) x[i1] < x[i2],...,<x[ik];
2) 1<=i1 < i2,...,<ik<=n

As an excellent program designer, you must know how to find the maximum length of the 
increasing sequense, which is defined as s. Now, the next question is how many increasing 
subsequence with s-length can you find out from the sequence X.

For example, in one case, if s = 3, and you can find out 2 such subsequence A and B from X.
1) A = a1, a2, a3. B = b1, b2, b3.
2) Each ai or bj(i,j = 1,2,3) can only be chose once at most.

Now, the question is:
1) Find the maximum length of increasing subsequence of X(i.e. s).
2) Find the number of increasing subsequence with s-length under conditions described (i.e. num).
 

Input
The input file have many cases. Each case will give a integer number n.The next line will 
have n numbers.
 

Output
The output have two line. The first line is s and second line is num.
 

Sample Input
4 3 6 2 5
 

Sample Output
2 2
题意:给出一串序列,求最长上升子序列和最长上升子序列的个数,要求每个数只出现一次。
思路:n^2dp求出第一个解,这时就标记了以每一个数为最后一个数的上升子序列的最大长度。因为要求每个数都只出现一次,所以相当于求最多的不相交路线,可用最大流解出。将每个点拆为i和i+1,连i到i+1的边,容量为1,然后源点到每个标记长度为1的边连一条边,容量为1,标记长度为最大长度的点到汇点连一条边,长度也为1。最后对于每个点,排在它后面的且标记长度比它大1的点之间连容量为1的边。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <cmath>
#include <cstdlib>
#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)
#define ll __int64
#define eps 1e-6

using namespace std;

const int INF = 1000000000;
const int maxn = 1005;

struct Edge
{
    int u, v, cap, flow, next;
} et[maxn*maxn];
int pre[maxn], cur[maxn], dis[maxn],cnt[maxn], low[maxn],  eh[maxn];
int a[maxn], dp[maxn];
int n, s, t, num;
void init()
{
    memset(eh, -1, sizeof(eh));
    num = 0;
}
void add(int u, int v, int cap, int flow)
{
    Edge e = {u, v, cap, flow, eh[u]};
    et[num] = e;
    eh[u] = num++;
}
void addedge(int u, int v, int cap)
{
    add(u, v, cap, 0);
    add(v, u, 0, 0);
}
int isap(int s, int t, int nv)
{
    int u, v, now, flow = 0;
    memset(cnt, 0, sizeof(cnt));
    memset(low, 0, sizeof(low));
    memset(dis, 0, sizeof(dis));
    for(u = 0; u <= nv; u++) cur[u] = eh[u];
    low[s] =INF, cnt[0] = nv, u = s;
    while(dis[s] < nv)
    {
        for(now = cur[u]; now != -1; now = et[now].next)
            if(et[now].cap - et[now].flow && dis[u] == dis[v = et[now].v] + 1) break;
        if(now != -1)
        {
            cur[u] = pre[v] = now;
            low[v] = min(et[now].cap - et[now].flow, low[u]);
            u = v;
            if(u == t)
            {
                for(; u != s; u = et[pre[u]].u)
                {
                    et[pre[u]].flow += low[t];
                    et[pre[u]^1].flow -= low[t];
                }
                flow += low[t];
                low[s] = INF;
            }
        }
        else
        {
            if(--cnt[dis[u]] == 0) break;
            dis[u] = nv, cur[u] = eh[u];
            for(now = eh[u]; now != -1; now = et[now].next)
                if(et[now].cap - et[now].flow && dis[u] > dis[et[now].v] + 1)
                    dis[u] = dis[et[now].v] + 1;
            cnt[dis[u]]++;
            if(u != s) u = et[pre[u]].u;
        }
    }
    return flow;
}
int main()
{
    while(~scanf("%d", &n))
    {
        init();
        s = 0;
        t = 2 * n + 1;
        for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
        for(int i = 1; i <= n; i++) dp[i] = 1;
        for(int i = 2; i <= n; i++)
        for(int j = 1; j < i; j++)
            if(a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
        //for(int i = 1; i <= n; i++) cout<<dp[i]<<endl;
        int ans = 0;
        for(int i = 1; i <= n; i++)
        if(dp[i] > ans) ans = dp[i];
        for(int i = 1; i <=n; i++)
        {
            addedge(i, i + n, 1);
            if(dp[i] == 1) addedge(s, i, 1);
            if(dp[i] == ans) addedge(i, t, 1);
            for(int j = i + 1; j <=n; j++)
            if(dp[j] == dp[i] + 1) addedge(i + n, j, 1);
        }
        printf("%d\n%d\n", ans, isap(s, t, t+1));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值