1. 程式人生 > >P2766 最長不下降子序列問題

P2766 最長不下降子序列問題

就是 如果 flow 輸入格式 rom hang chan names truct

\(\color{#0066ff}{題目描述}\)

?問題描述:

給定正整數序列x1,...,xn 。

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

(2)計算從給定的序列中最多可取出多少個長度為s的不下降子序列。

(3)如果允許在取出的序列中多次使用x1和xn,則從給定序列中最多可取出多少個長度為s的不下降子序列。

?編程任務:

設計有效算法完成(1)(2)(3)提出的計算任務。

\(\color{#0066ff}{輸入格式}\)

第1 行有1個正整數n,表示給定序列的長度。接下來的1 行有n個正整數n:x1, ..., xn。

\(\color{#0066ff}{輸出格式}\)

第1 行是最長不下降子序列的長度s。第2行是可取出的長度為s 的不下降子序列個數。第3行是允許在取出的序列中多次使用x1和xn時可取出的長度為s 的不下降子序列個數。

\(\color{#0066ff}{輸入樣例}\)

4
3 6 2 5

\(\color{#0066ff}{輸出樣例}\)

2
2
3

\(\color{#0066ff}{數據範圍與提示}\)

\(n\leq 500\)

\(\color{#0066ff}{題解}\)

第一問大水題\(O(n^2)\),LIS,暴力就行

第二問第三問要用網絡流

拆點,序列的每個點拆成<x,y>

因為我們要找最多的序列,每個長度為ans

第一問求出了f[i]為以i結尾的最長不下降子序列的長度

現在對於每個i

如果f[i]==1,則原點向\(i_x\)連容量為 1 的邊

如果f[i]==ans,則\(i_y\)向匯點連容量為 1 的邊

對於每一個\(i<j\),如果\(a[i] \leq a[j]\),並且\(f[j]==f[i]+1\),那麽就從\(i_y\)\(j_x\)連一條容量為1的邊

對於每個i,連一條\(i_x\)到$i_y&的邊

這樣就保證了流過去一個,就是一個合法的序列,而且長度恰=ans,不會重復

對於第三問,因為\(a_1,a_n\)無限使用,把1和n的一些邊變成inf就行了,在原基礎上再跑一遍就行了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#define _ 0
#define LL long long
inline LL in()
{
    LL x=0,f=1; char ch;
    while(!isdigit(ch=getchar()))(ch=='-')&&(f=-f);
    while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
    return x*f;
}
const int inf=0x7fffffff;
struct node
{
    int to,dis;
    node *nxt,*rev;
    node(int to=0,int dis=0,node *nxt=NULL):to(to),dis(dis),nxt(nxt){}
    void *operator new (size_t)
    {
        static node *S=NULL,*T=NULL;
        return (S==T&&(T=(S=new node[1024])+1024)),S++;
    }
};
typedef node* nod;
nod head[10500],cur[10500];
int dep[10500];
int n,s,t;
std::queue<int> q;
int a[10500];
inline void add(int from,int to,int dis)
{
    nod o=new node(to,dis,head[from]);
    head[from]=o;
}
inline void link(int from,int to,int dis)
{
    add(from,to,dis);
    add(to,from,0);
    head[from]->rev=head[to];
    head[to]->rev=head[from];
}
inline bool bfs()
{
    for(int i=s;i<=t;i++) dep[i]=0,cur[i]=head[i];
    dep[s]=1;
    q.push(s); 
    while(!q.empty())
    {
        int tp=q.front(); q.pop();
        for(nod i=head[tp];i;i=i->nxt)
            if(!dep[i->to]&&i->dis>0)
            {
                dep[i->to]=dep[tp]+1;
                q.push(i->to);
            }
    }
    return dep[t];
}
inline int dfs(int x,int change)
{
    if(x==t||!change) return change;
    int flow=0,ls;
    for(nod i=cur[x];i;i=i->nxt)
    {
        cur[x]=i;
        if(dep[i->to]==dep[x]+1&&(ls=dfs(i->to,std::min(change,i->dis))))
        {
            change-=ls;
            flow+=ls;
            i->dis-=ls;
            i->rev->dis+=ls;
            if(!change) break;
        }
    }
    return flow;
}
inline int dinic()
{
    int flow=0;
    while(bfs()) flow+=dfs(s,inf);
    return flow;
}
namespace partone
{
    int f[10505],ans;
    void main()
    {
        for(int i=1;i<=n;i++)
        {
            f[i]=1;
            for(int j=1;j<i;j++) 
                if(a[j]<=a[i]) f[i]=std::max(f[i],f[j]+1);
            ans=std::max(ans,f[i]);
        }
        printf("%d\n",ans);
    }
}
namespace parttwo
{
    int fuc;
    using namespace partone;
    void main()
    {
        for(int i=1;i<=n;i++)
        {
            link(i,i+n,1);
            if(f[i]==1) link(s,i,1);
            if(f[i]==ans) link(i+n,t,1);
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<i;j++)
                if(a[j]<=a[i]&&f[i]==f[j]+1) link(j+n,i,1);
        printf("%d\n",fuc=dinic());
    }
}
namespace partthree
{
    using namespace partone;
    void main()
    {
        link(1,1+n,inf),link(s,1,inf);
        if(f[n]==ans) link(n+n,t,inf),link(n,n+n,inf);
        printf("%d\n",parttwo::fuc+dinic());
    }
}

int main()
{
    n=in();
    s=1,t=(n<<1)+1;
    for(int i=1;i<=n;i++) a[i]=in();
    partone::main();
    parttwo::main();
    partthree::main();
    return 0;
}

P2766 最長不下降子序列問題