洛谷2766最長不下降子序列問題
阿新 • • 發佈:2018-12-19
題目連結:最長不下降子序列問題
這一個問題雖然有三小問,但是每一個小問題的連線非常緊密
對於第一問,直接\(O(n^2)\)水過,你要用\(O(nlogn)\)當然也可以啊
二三問考慮使用網路流求解
我們利用第一問中得到的dp關係來建圖:
很明顯的是這裡的每一個數只能用一次,所以我們將每一個點拆成兩個點,之間連一條容量為1的邊,其中一個點作為入點,另一個點作為出點(這一點十分重要,是網路流中一個常規且重要的操作)
dp的起始點也就是\(dp[i]=1\)時,我們從\(s\)向\(i\)連一條容量為1的邊
dp的終點也就是\(dp[i]=s\)時,我們從\(i\)向\(t\)連一條容量為1的邊
在中間出現轉移的時候(也就是滿足\(dp[j]+1=dp[i]\ \&\&\ a[j]<=a[i]\)時),我們從\(j\)向\(i\)連一條容量為1的邊
這樣直接去跑網路流的話第二問就解決了
對於第三問,最直接的思路就是對這個圖進行改造或重建,由於此時\(x_1\)和\(x_n\)可以使用多次,我們可以將\(1\)中兩個點所連的邊的容量以及\(s\)到1的邊的容量改成INF。
同時如果\(n\)與\(t\)有連邊的話就同樣進行上述操作
一個小技巧是:我們並不需要重新建圖,由於我們並未該小流量,我們可以直接加上上面的幾條邊到殘量網路中,然後繼續跑網路流,將兩次的答案相加就是第三問的答案了
#include<iostream> #include<string> #include<string.h> #include<stdio.h> #include<algorithm> #include<math.h> #include<vector> #include<queue> #include<map> using namespace std; #define maxd 1e9+7 struct network_flows{ struct node{ int from,to,nxt,flow; }sq[100100]; int all,dep[100100],head[100100],cur[100100],n,m,s,t; bool vis[100100]; void init(int n) { this->s=2*n+1;this->t=2*n+2; this->n=2*n+2;this->all=1; memset(head,0,sizeof(head)); } void add(int u,int v,int w) { all++;sq[all].from=u;sq[all].to=v;sq[all].nxt=head[u];sq[all].flow=w;head[u]=all; all++;sq[all].from=v;sq[all].to=u;sq[all].nxt=head[v];sq[all].flow=0;head[v]=all; } bool bfs() { queue<int> q;int i; memset(vis,0,sizeof(vis)); vis[s]=1;q.push(s);dep[s]=0; while (!q.empty()) { int u=q.front();q.pop(); for (i=head[u];i;i=sq[i].nxt) { int v=sq[i].to; if ((!vis[v]) && (sq[i].flow)) { vis[v]=1;dep[v]=dep[u]+1;q.push(v); } } } if (!vis[t]) return 0; for (i=1;i<=n;i++) cur[i]=head[i]; return 1; } int dfs(int now,int to,int lim) { if ((!lim) || (now==to)) return lim; int i,sum=0; for (i=head[now];i;i=sq[i].nxt) { int v=sq[i].to; if (dep[now]+1==dep[v]) { int f=dfs(v,to,min(lim,sq[i].flow)); if (f) { lim-=f;sum+=f; sq[i].flow-=f; sq[i^1].flow+=f; if (!lim) break; } } } return sum; } int work() { int ans=0; while (bfs()) ans+=dfs(s,t,maxd); return ans; } }dinic; int n,a[1010],dp[1010],s,t,ans1; int read() { int x=0,f=1;char ch=getchar(); while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();} while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();} return x*f; } void init() { n=read();int i,j; dinic.init(n); for (i=1;i<=n;i++) a[i]=read(); for (i=1;i<=n;i++) { int best=0; for (j=1;j<i;j++) if ((a[j]<=a[i]) && (dp[j]>dp[best])) best=j; //cout << i << " " << best << endl; dp[i]=dp[best]+1; } for (i=1;i<=n;i++) ans1=max(dp[i],ans1); printf("%d\n",ans1); } void make_sq() { s=2*n+1;t=2*n+2;int i,j; //for (i=1;i<=n;i++) printf("%d ",dp[i]);cout << endl; for (i=1;i<=n;i++) { dinic.add(i,i+n,1); if (dp[i]==1) dinic.add(s,i,1); if (dp[i]==ans1) dinic.add(i+n,t,1); for (j=1;j<i;j++) if ((a[i]>=a[j]) && (dp[i]==dp[j]+1)) dinic.add(j+n,i,1); } } void work() { int ans2=dinic.work(); dinic.add(s,1,maxd);dinic.add(1,n+1,maxd); if (dp[n]==ans1) {dinic.add(n+n,t,maxd);dinic.add(n,n+n,maxd);} int ans3=dinic.work(); printf("%d\n%d\n",ans2,ans2+ans3); } int main() { init(); make_sq(); work(); return 0; }