【LIS+最大流】LOJ - #6005. 最長遞增子序列
阿新 • • 發佈:2018-12-25
題目連結<https://loj.ac/problem/6005>
題意:
給定正整數序列 x1∼xn ,以下遞增子序列均為非嚴格遞增。
- 計算其最長遞增子序列的長度s。
- 計算從給定的序列中最多可取出多少個長度為 s 的遞增子序列。
- 如果允許在取出的序列中多次使用x1和xn,則從給定序列中最多可取出多少個長度為s的遞增子序列。
題解:
- 第一問直接秒掉,寫成upperbound即可。
- 第二問基於第一問的dp陣列,源點與dp為1的連一條權值為1的邊,dp為s的與匯點連一條權值為1的邊。對於每一個點i,與它後面的點j且a[i]<=a[j]&&dp[j]==dp[i]+1連線一條權值為1的邊。這樣跑網路流就相當於模擬了。因為只能每個點只能取一次,所以還要拆點(其實不拆點也能水過)。
- 第三問就把兩個點的容量限制變為無窮就行了。
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=1e5+7; const int inf=1e9+7; struct Edge{ int v,w,nxt; Edge(int v=0,int w=0,int nxt=0):v(v),w(w),nxt(nxt){} }e[N*30]; int edn,sp,tp; int p[N],d[N],c[N]; void add(int u,int v,int w){ e[++edn]=Edge(v,w,p[u]);p[u]=edn; e[++edn]=Edge(u,0,p[v]);p[v]=edn; } bool bfs(){ memset(d,-1,sizeof(d));d[sp]=0; queue<int>q;q.push(sp); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=p[u];~i;i=e[i].nxt){ int v=e[i].v; if(d[v]==-1&&e[i].w){ d[v]=d[u]+1; q.push(v); if(v==tp) return true; } } } return ~d[tp]; } int dfs(int u,int b){ if(u==tp) return b; int r=0; for(int i=c[u];~i;i=e[i].nxt){ int v=e[i].v; if(e[i].w&&d[v]==d[u]+1){ int x=min(e[i].w,b-r); c[u]=i; x=dfs(v,x); r+=x; e[i].w-=x; e[i^1].w+=x; if(r==b) break; } } if(!r) d[u]=-2; return r; } int dinic(){ int tot=0,t; while(bfs()){ memcpy(c,p,sizeof(p)); while(t=dfs(sp,inf)) tot+=t; } return tot; } int a[N],g[N],dp[N],n; int main(){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); g[i]=inf; } g[n]=inf; for(int i=0;i<n;i++){ int k=upper_bound(g+1,g+1+n,a[i])-g; dp[i]=k; g[k]=a[i]; } int ans=0; for(int i=0;i<n;i++) ans=max(ans,dp[i]); printf("%d\n",ans); memset(p,-1,sizeof(p));edn=-1; sp=2*n+1,tp=sp+1; for(int i=0;i<n;i++){ if(dp[i]==1) add(sp,i,1); if(dp[i]==ans) add(i+n,tp,1); for(int j=i+1;j<n;j++){ if(a[i]<=a[j]&&dp[j]==dp[i]+1) add(i+n,j,1); } add(i,i+n,1); } printf("%d\n",dinic()); memset(p,-1,sizeof(p));edn=-1; sp=n*2+1,tp=sp+1; for(int i=0;i<n;i++){ if(dp[i]==1){ if(i==0) add(sp,i,inf); else add(sp,i,1); } if(dp[i]==ans){ if(i==n-1) add(i,tp,inf); else add(i+n,tp,1); } for(int j=i+1;j<n;j++){ if(a[i]<=a[j]&&dp[j]==dp[i]+1) add(i+n,j,1); } if(i==0||i==n-1) add(i,i+n,inf); else add(i,i+n,1); } printf("%d\n",dinic()); }