hdu 3998 Sequence【dp+拆点+最大流--------Dinic】

Sequence

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

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

Source

2011 Multi-University Training Contest 16 - Host by TJU

 

题目大意:

给你一个长度为n的序列,求其最长上升子序列的个数maxn,然后如果每一个数字只能用一次的条件下,能够组成几个长度为maxn的上升子序列。


思路:


1、根据Discuss中的提示,N并不会很大,(Discuss中说是2000左右),那么我们可以N^2求最长上升子序列。并且维护maxn。

设定dp【i】表示序列进行到第i个位子,并且以a【i】结尾的最长上升子序列的个数。

那么dp【i】=max(dp【i】,dp【j】+1)(a【i】>a【j】);初始化dp【i】=1;


2、然后我们能够得到一个dp【】数组,那么我们建图方式如下:

①首先每个数字只能用一次,那么我们将这个数字拆成两个点,i,i+n,并且建立从i->i+n一条边,设定流为1,表示这个点只能用一次。

②建立源点,将源点连入各个dp【i】==1的点,设定流为INF。

③建立汇点,将各个dp【i】==maxn的点连入汇点,设定流为INF。

④将所有满足:dp【i】==dp【j】+1&&a【i】>a【j】的i,j点对加入网络中,建立从j+n->i的一条边,设定流为INF。


3、建好图之后跑最大流,得到的最大流的值就是能够组成的序列的个数,那么我们输出maxn,maxflow即可。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<iostream>
using namespace std;
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[3000500];
int cur[100060];
int divv[100060];
int head[100060];
int a[100060];
int dp[100060];
int n,cont,ss,tt,maxn;
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void LIS()
{
    memset(dp,0,sizeof(dp));
    dp[1]=1;
    for(int i=2;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[i]>a[j])
            {
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
        maxn=max(maxn,dp[i]);
    }
}
void getmap()
{
    cont=0;
    memset(head,-1,sizeof(head));
    ss=2*n+1;
    tt=ss+1;
    for(int i=1;i<=n;i++)
    {
        add(i,i+n,1);
        add(i+n,i,0);
        if(dp[i]==1)
        {
            add(ss,i,0x3f3f3f3f);
            add(i,ss,0);
        }
        if(dp[i]==maxn)
        {
            add(i+n,tt,0x3f3f3f3f);
            add(tt,i+n,0);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            if(dp[j]==dp[i]+1&&a[j]>a[i])
            {
                add(i+n,j,0x3f3f3f3f);
                add(j,i+n,0);
            }
        }
    }
}
int makedivv()
{
    queue<int >s;
    s.push(ss);
    memset(divv,0,sizeof(divv));
    divv[ss]=1;
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w&&divv[v]==0)
            {
                divv[v]=divv[u]+1;
                s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    if(u==tt)return maxflow;
    int ret=0;
    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        int w=e[i].w;
        if(w&&divv[v]==divv[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
void Dinic()
{
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,0x3f3f3f3f,tt);
    }
    printf("%d\n",maxn);
    printf("%d\n",ans);
}
int main()
{
    while(~scanf("%d",&n))
    {
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        maxn=0;
        LIS();
        getmap();
        Dinic();
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值