[网络流24题]最长递增子序列问题

Description

给定正整数序列$x_1,x_2,...,x_n$.

$1.$计算其最长递增子序列的长度$L$。

$2.$计算从给定的序列中最多可取出多少个长度为$L$的递增子序列。

$3.$如果允许在取出的序列中多次使用$x_1,x_n$,则从给定序列中最多可取出多少个长度为$L$的递增子序列。

Input

第$1$行有$1$个正整数$n$,表示给定序列的长度。

接下来的$1$行有$n$个正整数$x_1,x_2,...,x_n$。

Output

第$1$行是最长递增子序列的长度$L$。

第$2$行是可取出的长度为$L$的递增子序列个数。

第$3$行是允许在取出的序列中多次使用$x_1,x_n$时可取出的长度为$L$的递增子序列个数。

Sample Input

4
3 6 2 5

Sample Output

2
2
3

HINT

$n<500$

Solution

  • 第一问:

最简单的$LIS$的$DP$.

  • 第二问:

将每个数$a_i$进行拆点:$x_i,y_i$,中间连一条边限制其流量(即使用次数).

从$x_i$向$y_i$连一条容量为$1$的有向边.

如果$f[i]=L$,从$s$向$x_i$连一条容量为$1$的有向边.

如果$f[i]=1$,从$y_i$向$t$连一条容量为$1$的有向边.

如果$i<j,a_i<a_j,f[i]+1=f[j]$,从$y_j$向$x_i$连一条容量为$1$的有向边.

求最大流.

  • 第三问:

将$(x_1,y_1),(x_n,y_n),(x_1,t),(s,x_n)$的值改成$+\infty$.

求最大流.

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 500
#define M 500000
using namespace std;
struct graph{
    int nxt,to,f;
}e[M];
int a[N],f[N],g[N<<1],dep[N<<1],n,m,s,t,cnt=1;
queue<int> q;
inline void addedge(int x,int y,int f){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f;
}
inline void adde(int x,int y,int f){
    addedge(x,y,f);addedge(y,x,0);
}
inline bool bfs(int u){
    memset(dep,0,sizeof(dep));
    q.push(u);dep[u]=1;
    while(!q.empty()){
        u=q.front();q.pop();
        for(int i=g[u];i;i=e[i].nxt)
            if(e[i].f>0&&!dep[e[i].to]){
                q.push(e[i].to);
                dep[e[i].to]=dep[u]+1;
            }
    }
    return dep[t];
}
inline int dfs(int u,int f){
    int ret=0;
    if(u==t) return f;
    for(int i=g[u],d;i&&f;i=e[i].nxt)
        if(e[i].f>0&&dep[e[i].to]>dep[u]){
            d=dfs(e[i].to,min(f,e[i].f));
            ret+=d;f-=d;e[i].f-=d;e[i^1].f+=d;
        }
    return ret;
}
inline int dinic(){
    int ret=0;
    while(true){
        if(!bfs(s)) return ret;
        ret+=dfs(s,M);
    }
}
inline void Aireen(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;++i){
        f[i]=1;
        for(int j=1;j<i;++j)
            if(a[j]<a[i])
                f[i]=max(f[i],f[j]+1);
        m=max(m,f[i]);
    }
    printf("%d\n",m);
    s=(n<<1)+1;t=s+1;
    for(int i=1;i<=n;++i){
        adde(i,i+n,1);
        if(f[i]==1) adde(i+n,t,1);
        if(f[i]==m) adde(s,i,1);
        for(int j=1;j<i;++j)
            if(a[j]<a[i]&&f[j]+1==f[i])
                adde(i+n,j,1);
    }
    printf("%d\n",dinic());
    cnt=1;memset(g,0,sizeof(g));
    for(int i=1,c,d,k;i<=n;++i){
        c=d=k=1;
        if(i==1) c=k=M;
        if(i==n) d=k=M;
        adde(i,i+n,k);
        if(f[i]==1) adde(i+n,t,c);
        if(f[i]==m) adde(s,i,d);
        for(int j=1;j<i;++j)
            if(a[j]<a[i]&&f[j]+1==f[i])
                adde(i+n,j,1);
    }
    printf("%d\n",dinic());
}
int main(){
    freopen("alis.in","r",stdin);
    freopen("alis.out","w",stdout);
    Aireen();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
posted @ 2017-01-01 11:58  Aireen_Ye  阅读(200)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.