nyoj 765 又见单调递增最长子序列

又见最长单调递增子序列

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述

给一个序列 X(x[1] , x[2] , x[3] ……) ,可以找出他的任意一个单调递增子序列如 x[i1] , x[i2] , x[i3] …… x[in]

它必须满足以下条件。

(1) X[i1] < X[i2] < …… < X[in];

(2) 1<= i1 <i2 < …… < in <= n

问题是:

(1)找到最长的单调递增子序列 ,输出长度 len

(2)在每个元素最多只能用一次的情况下,找出长度为len 的单调递增子序列有多少个,输出个数sum。

输入
多组测试数据,不超过100组.
每组输入数据包含两行。
第一行一个数字n,代表序列的长度,第二行包含 n 个数字ai,代表序列中每个元素的值。
n <= 500 , ai <= 10000
输出

每组测试数据输出有两行。第一行输出len,第二行输出sum。



思路 : 将所有在单调递增最长子序列上的点建立边,将长度最长为1的点与源点建边,长度为len 的与汇点建边,中间则将 i,j,建边(dp[i]+1==dp[j]),边的容量为1 ,为保证只用一次,将一个点拆成两个点,容量为1 



#include<bits/stdc++.h>
using namespace std;
int min ( int x,int y)
{
    return x<y?x:y;
}
const int inf  = 0x3f3f3f3f;
const int maxn = 10000;//点的数目
int e,head[maxn];
int level[maxn];
int q[maxn];
int a[maxn];
int dp[maxn];
struct node
{
    int to ,val,next;
} edge[200005];//边
void add_edge(int x,int y,int w)
{
    edge[e].to = y;
    edge[e].val = w;
    edge[e].next =head[x];
    head[x]= e++;
    edge[e].to  = x;
    edge[e].val = 0;
    edge[e].next = head[y];
    head[y] = e++;
}
bool bfs(int s,int t)
{
    int front =0;
    int rear = 0;
    int top,k;
    q[rear++] = s;
    memset(level,0,sizeof(level));
    level[s] =1;
    while(front<rear)
    {
        top = q[front++];
        if(top==t)
            return true;
        for(int k = head[top]; k!=-1; k=edge[k].next)
        {
            if(!level[edge[k].to]&&edge[k].val>0)
            {
                level[edge[k].to] = level[top]+1;
                q[rear++] = edge[k].to;
            }
        }

    }
    return false;
}
int dfs(int now,int maxf,int t)
{
    int ret =0,f,k;
    if(now==t)
        return maxf;
    for(int k = head[now]; k!=-1; k = edge[k].next)
    {
        if(level[edge[k].to]==level[now]+1&&edge[k].val>0)
        {
            f = dfs(edge[k].to,min(maxf -ret,edge[k].val),t);
            edge[k].val-=f;
            edge[k^1].val+=f;
            ret+=f;
            if(ret==maxf)
                return ret ;

        }
    }
    if(ret==0)
        level[now] = 0;
    return ret;
}
int dinic(int s,int t)
{
    int ans = 0;
    while(bfs(s,t))
        ans+=dfs(s,inf,t);
    return ans;
}
int  main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(head,-1,sizeof(head));
        memset(dp,0,sizeof(dp));
        e = 0;
        for(int i = 1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        int len = 1;
        for(int i = 1; i<=n; i++)
        {
            dp[i] = 1;
            for(int j= i-1; j>=1; j--)
            {
                if(a[i]>a[j]&&dp[i]<dp[j]+1)
                {
                    dp[i]= dp[j]+1;
                }
            }
            len = max(dp[i],len);
            int v = dp[i];
            for(int j= i-1; j>=1; j--)
            {
                if(a[i]>a[j]&&v==dp[j]+1)
                {
                    add_edge(j+n,i,1);
                }
            }
        }
        if(len==1)
        {
            printf("%d\n",len);
            printf("%d\n",n);
            continue;
        }
        int s = 0;
        int t  = 2*n+1;
        for(int i= 1; i<=n; i++)
        {
            if(dp[i]==len)
            {
                add_edge(i+n,t,1);
            }
            if(dp[i]==1)
            {
                add_edge(s,i,1);
            }
            add_edge(i,i+n,1);
        }
        printf("%d\n",len);
        printf("%d\n",dinic(s,t));

    }
    return 0;
}










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值