题解:P2766 最长不下降子序列问题

传送门

给定正整数序列x1,…,xn 。
(1)计算其最长不下降子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。

对于这个题目,首先可以很简单的用DP求出第一个问题的答案,不过这个地方不能用nlogn的算法,因为这样不利于处理前后关系,我们采用朴素的n2 算法,记住它所转移过来的点。
对于第二个问题,运用第一次DP处理出来的关系进行连边,每个流量都是1
第三个问题同样如此,唯一不同就是首尾和ST的连边是INF

int c[N],f[N],ans=0;
int main () {
    n=read();
    s=n+1,t=n+2;
    for (int i=1; i<=n; ++i) c[i]=read();
 
    for (int i=1; i<=n; ++i) f[i]=1;
    for (int i=1; i<=n; ++i) {
    	for (int j=1; j<i; ++j) {
    		if (c[i]>=c[j]) 
    			f[i]=max(f[i], f[j]+1);
        }
        if (f[i]==1) add(s,i,1);
        else for (int j=1; j<i; ++j) 
            if (c[i]>=c[j]&&f[j]==f[i]-1) 
                add(j,i,1)
        ans=max(ans,f[i]);
    }
    for (int i=1; i<= n; ++i) 
        if (f[i]==ans) add(i,t,1);
    cout<<ans<<endl;
    
    int in;
    while (bfs ()) {while ((in=dinic (s,INF))) maxf+=in;} 
    cout<<maxf<<endl;
    
    memset(head,0,sizeof(head));
    memset(a,0,sizeof(a));
    
    maxf=0;
    add(s, 1, INF);
    for (int i=1; i<=n; ++i) f[i] = 1;
    for (int i=2; i<=n; ++i) {
    	for (int j=1; j<i; ++j) 
    		if (c[i]>=c[j]) 
    			f[i]=max(f[i],f[j]+1);
    			
        if (f[i]==1) add(s,i,1);
        else for (int j=1; j<i; ++j) 
            if (c[i]>=c[j]&&f[j]==f[i]-1) 
                add(j,i,1);
    }
    for (int i=1; i<=n-1; ++i) 
        if (f[i]==ans) add(i,t,1);
        
    if (f[n]==ans) add(n,t,INF);
    
    while (bfs ()) {while ((in=dinic (s,INF))) maxf+=in;} 
    cout<<maxf<<endl;
    
    return 0;
}

你可能感兴趣的:(题解,网络流)