• P2766 最长不下降子序列问题


    题目描述

    «问题描述:

    给定正整数序列x1,...,xn 。

    (1)计算其最长不下降子序列的长度s。

    (2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。

    (3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。

    «编程任务:

    设计有效算法完成(1)(2)(3)提出的计算任务。

    输入输出格式

    输入格式:

    第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。

    输出格式:

    第1 行是最长不下降子序列的长度s。第2行是可取出的长度为s 的不下降子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的不下降子序列个数。

    输入输出样例

    输入样例#1: 
    4
    3 6 2 5
    输出样例#1: 
    2
    2
    3

    说明

    n≤500

    Solution:

      本题简单dp+最大流。

      第一问直接煞笔dp。

      第二问求最多取出多少个长度为$x=max(f[i])$的子序列,因为每个数要么不选要么只选1次,这样有上下界的题目,考虑拆点跑最大流咯,将每个数拆成$i ightarrow i'$连流量为1,若$f[i]==1$则$s ightarrow i$连流量为1,若$f[i]==x$则$i' ightarrow t$连流量为1,若$f[i]==f[j]+1,j<i,a_jleq a_i$则$j' ightarrow i$连流量为1,跑最大流就好了。

      第三问可以重复用$a_1$和$a_n$,那么改变的就是与这两点直接相关的边的流量了,我们在第二问的答案基础上,加入新边,$1 ightarrow 1'$流量inf,$n ightarrow n'$流量inf,$s ightarrow 1$流量inf,若$f[n]==x$则$n' ightarrow t$流量inf,再跑下最大流就好了。

      (实际上本题第三问有bug,比如最长不下降子序列长度为1,那么第三问答案就应该是inf了,inf不确定,于是乎出锅咯!反正实践证明没这数据,嘿嘿嘿!>.@_@.<)

    代码:

    /*Code by 520 -- 9.1*/
    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define RE register
    #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
    #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
    using namespace std;
    const int N=1050,M=2500005,inf=0x7fffffff;
    int n,a[N],f[N],s,t=1001;
    int h[N],dis[N],to[M],net[M],w[M],cnt=1;
    int ans1,ans2;
    
    il void add(int u,int v,int c){
        to[++cnt]=v,net[cnt]=h[u],w[cnt]=c,h[u]=cnt;
        to[++cnt]=u,net[cnt]=h[v],w[cnt]=0,h[v]=cnt;
    }
    
    il bool bfs(){
        queue<int>q;
        memset(dis,-1,sizeof(dis));
        q.push(s),dis[s]=0;
        while(!q.empty()){
            RE int u=q.front();q.pop();
            for(RE int i=h[u];i;i=net[i])
                if(dis[to[i]]==-1&&w[i]) dis[to[i]]=dis[u]+1,q.push(to[i]);
        }
        return dis[t+1]!=-1;
    }
    
    int dfs(int u,int op){
        if(u==t+1)return op;
        int flow=0,used=0;
        for(RE int i=h[u];i;i=net[i]){
            int v=to[i];
            if(dis[v]==dis[u]+1&&w[i]){
                used=dfs(v,min(op,w[i]));
                if(!used)continue;
                flow+=used,op-=used;
                w[i]-=used,w[i^1]+=used;
                if(!op)break;
            }
        }
        if(!flow) dis[u]=-1;
        return flow;
    }
    
    il void init(){
        scanf("%d",&n);
        For(i,1,n) scanf("%d",&a[i]),f[i]=1;
        For(i,1,n) For(j,1,i-1) if(a[i]>=a[j]) f[i]=max(f[i],f[j]+1);
        For(i,1,n) ans1=max(ans1,f[i]);
        printf("%d
    ",ans1);
        For(i,1,n) {
            add(i,i+n,1);
            if(f[i]==1) add(i+n,t,1);
            if(f[i]==ans1) add(s,i,1);
        }
        add(t,t+1,inf);
        For(i,1,n) For(j,1,i-1) if(f[i]==f[j]+1&&a[i]>=a[j]) add(i+n,j,1);
        while(bfs()) ans2+=dfs(s,inf);
        printf("%d
    ",ans2);
        add(n+1,t,inf),add(1,n+1,inf),add(n,n<<1,inf);
        if(f[n]==ans1) add(s,n,inf);
        while(bfs()) ans2+=dfs(s,inf);
        printf("%d
    ",ans2);
    }
    
    int main(){
        init();
        return 0;
    }
  • 相关阅读:
    SpringJMS解析2-JmsTemplate
    SpringJMS解析1-使用示例
    SpringHttpInvoker解析3-客户端实现
    算法笔记_084:蓝桥杯练习 11-1实现strcmp函数(Java)
    算法笔记_083:蓝桥杯练习 合并石子(Java)
    算法笔记_082:蓝桥杯练习 12-1三角形(Java)
    算法笔记_081:蓝桥杯练习 算法提高 矩阵乘法(Java)
    算法笔记_080:蓝桥杯练习 队列操作(Java)
    算法笔记_079:蓝桥杯练习 区间k大数查询(Java)
    算法笔记_078:蓝桥杯练习 最大最小公倍数(Java)
  • 原文地址:https://www.cnblogs.com/five20/p/9575377.html
Copyright © 2020-2023  润新知