P2766 最長不下降子序列問題
阿新 • • 發佈:2018-12-13
就是 如果 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 最長不下降子序列問題