关闭

【网络流24题】最长递增子序列问题

224人阅读 评论(0) 收藏 举报
分类:

(网络流24题大多需要spj,所以需要一个有spj的oj,本系列代码均在www.oj.swust.edu.cn测试通过)
这道题据说在codeforces上的数据变成了最长不降,但是本文附上的oj没有这种情况。
这道题的题意就是求出最长上升序列长度,然后每个数只能取一个的情况下最多有多少种方案能取出这么长的序列,第一个数和最后一个数无限多的时候能取出多少个。
首先第一问dp就可以了,而且数据不大,最简单的dp即可。
然后第二问出现了一个数只能选一次的问题,那么就到了网络流的领域了,我们这样实现,将一个点拆分为一个入点和一个出点,入点和出点之间连一条1的流,如果以一个数为尾的最长序列长度为第一问答案的话,那么就从源点向该点连一条容量为1的流,如果最长序列长度为1的话就向汇点连接一条容量为一的流,两个点满足i < j && a[i] < a[j] && f[j]==f[i]+1 则从j向i连接一条容量为1的流,这样的话每一条流都是一个最长的序列,有几条这样的序列第二问的答案就是几。
第三问主要的变化就是1和n的数量发生了变化,那我们只需要将(1,1),(n,n)(s,1)(1,t)(s,n)(n,t)(如果有的话)的流量改为INF再求一边增广路即可。
注意当最长上升子序列为1的情况,这时候理论方案为无穷大(因为1,n无限多),但是根据数据来看只算了一个,将答案取余INF即可

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<iomanip>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 100000000
struct bian
{
    int l,r,f;
}a[1000000];
int fir[1000000];
int nex[1000000];
int d[100000];
int s=0,t=99999;
bool bfs()
{
    static int dui[1000000];
    memset(d,-1,sizeof(d));
    int top=1,my_final=2;
    dui[top]=s;
    d[s]=1;
    while(top<my_final)
    {
        int u=dui[top++];
        for(int o=fir[u];o;o=nex[o])
        {
            if(d[a[o].r]==-1 && a[o].f)
            {
                dui[my_final++]=a[o].r;
                d[a[o].r]=d[u]+1;
                if(a[o].r==t) return true;
            }
        }
    }
    return false;
}
int dinic(int u,int flow)
{
    if(u==t) return flow;
    int left=flow;
    for(int o=fir[u];o&&left;o=nex[o])
    {
        if(a[o].f && d[a[o].r]==d[u]+1)
        {
            int temp=dinic(a[o].r,min(left,a[o].f));
            if(!temp) d[a[o].r]=-1;
            left-=temp;
            a[o].f-=temp;
            a[o^1].f+=temp;
        }
    }
    return flow-left;
}
int tot=1;
void add_edge(int l,int r,int f)
{
    a[++tot].l=l;
    a[tot].r=r;
    a[tot].f=f;
    nex[tot]=fir[l];
    fir[l]=tot;
}
int dui[1000000];
int top=0;
int f[1000];
int v[1000];
int main()
{
    int n;
    scanf("%d",&n);
    v[0]=-2147483647;
    f[0]=0;
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&v[i]);
        for(int j=0;j<i;j++) if(v[j]<v[i]) f[i]=max(f[i],f[j]+1);
        ans=max(ans,f[i]);
    }
    for(int i=1;i<=n;i++)
    {
        add_edge(i,n+i,1);
        if(i==1 || i==n) dui[++top]=tot;
        add_edge(n+i,i,0);
    }
    for(int i=1;i<=n;i++)
    {
        if(f[i]==ans)
        {
            add_edge(s,i,1);
            if(i==1 || i==n) dui[++top]=tot;
            add_edge(i,s,0);
        }
        if(f[i]==1)
        {
            add_edge(i+n,t,1);
            if(i==1 || i==n) dui[++top]=tot;
            add_edge(t,i+n,0);
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
        {
            if(v[j]<v[i] && f[i]==f[j]+1)
            {
                add_edge(i+n,j,1);
                add_edge(j,i+n,0);
            }
        }
    cout<<ans<<endl;
    int trueans=0;
    while(bfs()) trueans+=dinic(s,INF);
    cout<<trueans<<endl;
    for(int i=1;i<=top;i++)
        a[dui[i]].f=INF;
    while(bfs()) trueans+=dinic(s,INF);
    if(trueans>INF) trueans=trueans%INF;
    cout<<trueans<<endl;
    return 0;
}
0
3

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:19295次
    • 积分:1262
    • 等级:
    • 排名:千里之外
    • 原创:121篇
    • 转载:1篇
    • 译文:0篇
    • 评论:5条
    最新评论