[網絡流24題] 最長遞增子序列
[網絡流24題] 最長遞增子序列
«問題描述:
給定正整數序列x1,..., xn。
(1)計算其最長遞增子序列的長度s。
(2)計算從給定的序列中最多可取出多少個長度為s的遞增子序列。
(3)如果允許在取出的序列中多次使用x1和xn,則從給定序列中最多可取出多少個長
度為s的遞增子序列。
註意:這裏的最長遞增子序列即最長不下降子序列!!!
«編程任務:
設計有效算法完成(1)(2)(3)提出的計算任務。
«數據輸入:
由文件alis.in提供輸入數據。文件第1 行有1個正整數n(n<=500),表示給定序列的長度。接
下來的1 行有n個正整數x1,..., xn。
程序運行結束時,將任務(1)(2)(3)的解答輸出到文件alis.out中。第1 行是最長
遞增子序列的長度s。第2行是可取出的長度為s 的遞增子序列個數。第3行是允許在取出
的序列中多次使用x1和xn時可取出的長度為s 的遞增子序列個數。
輸入文件示例 輸出文件示例
alis.in
4
3 6 2 5
alis.out
2
2
3
題目描述的不是很清楚,不是遞增,是不下降,另外無限用事實上指的是1和n可以被用於多個不下降序列中,可以重復使用,而其他點只可以用一次。
對於第一問,隨便求一下就行...
對於後兩問,我們想到網絡流的方法(畢竟網絡流24題),如何限制一個點的經過次數呢?我們可以把它拆成兩個點,在兩個點之間連一條權值為x的邊,表示這個點最多經過x次。那如何建圖呢,對於兩個位置i,j,如果a[i]<=a[j]並且i<j並且g[i]+1=g[j],那麽我們就可以在他們之間連一條邊,想一下也很簡單。那麽與源點(S)和匯點(T)如何連邊呢?
對於第二問的情況,連1就行了,因為最多用一次。
對於第三問,非無限取的點和第二問一樣,對於無限取的點,他與S相連的權值至少應該為他可能作為起點出現的次數a,與T相連的權值至少應該為他可能作為終點出現的次數b。對於只與S或者只與T相連的點,這種點的存在說明了最長不下降子序列長度不為1,我們邊的權值可以取任意大於等於a或者b數字,因為其他的點會對他做出限制,他取大一點也沒關系,反正也流不了那麽多,但對於s與t均連在一個點上的情況,說明最長不下降序列長度為1,這時候a,b中比較小的那一個(其實這種情況全是1),就必須取他本身,也就是1,才能“限制”住,不然的話他的流量就變成inf了。而1這個點又比較特殊,如果他可以作為終點出現,那麽說明最長不下降序列長度為1,也就一定可以作為起點出現,那麽1和t的連邊權值一定是1,n點同理,與s連邊權值一定是1,。
語文比較差,,,可能講不清楚。也可能講的就是錯的qwq,因為我yy了一下午也就很牽強的說服了自己。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=1e6;//隨便開... 4 int n,a[inf],f[inf],g[inf],top; 5 int tot,fi[inf],to[inf],next[inf],cost[inf],rev[inf]; 6 int ans,que[inf],head,tail,dep[inf],cur[inf]; 7 void slove1(){ 8 for(int i=1;i<=n;i++){ 9 if(a[i]>=f[top])f[++top]=a[i],g[i]=top; 10 else { 11 int l=1,r=top; 12 while(l!=r){ 13 int mid=(l+r)>>1; 14 if(f[mid]<=a[i])l=mid+1; 15 else r=mid; 16 } 17 f[l]=a[i]; 18 g[i]=l; 19 } 20 } 21 printf("%d\n",top); 22 } 23 void edge_add(int x,int y,int z){ 24 to[++tot]=y;next[tot]=fi[x]; 25 fi[x]=tot;cost[tot]=z;rev[tot]=tot+1; 26 to[++tot]=x;next[tot]=fi[y]; 27 fi[y]=tot;cost[tot]=0;rev[tot]=tot-1; 28 } 29 bool bfs(){ 30 for(int i=1;i<=n*2+2;i++)cur[i]=fi[i],dep[i]=-1; 31 dep[1]=0; 32 head=1;tail=0; 33 que[++tail]=1; 34 while(head<=tail){ 35 int u=que[head++]; 36 for(int i=fi[u];i;i=next[i]) 37 if(cost[i]&&dep[to[i]]==-1) 38 dep[to[i]]=dep[u]+1, 39 que[++tail]=to[i]; 40 } 41 return dep[n*2+2]!=-1; 42 } 43 int dfs(int x,int f){ 44 if(x==n*2+2)return f; 45 for(int i=cur[x];i;i=next[i]){ 46 cur[x]=i; 47 if(cost[i]&&dep[to[i]]==dep[x]+1){ 48 int g=dfs(to[i],min(f,cost[i])); 49 if(g){ 50 cost[i]-=g; 51 cost[rev[i]]+=g; 52 return g; 53 } 54 } 55 } 56 return 0; 57 } 58 void dinic(){ 59 ans=0; 60 int f; 61 while(bfs()) 62 while(f=dfs(1,0x3fffffff))ans+=f; 63 printf("%d\n",ans); 64 } 65 int main() 66 { 67 freopen("alis.in","r",stdin); 68 freopen("alis.out","w",stdout); 69 // freopen("1.txt","r",stdin); 70 scanf("%d",&n); 71 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 72 slove1(); 73 for(int i=1;i<=n;i++){ 74 edge_add(i<<1,i<<1|1,1); 75 if(g[i]==1)edge_add(1,i<<1,1); 76 if(g[i]==top)edge_add(i<<1|1,n*2+2,1); 77 } 78 for(int i=1;i<n;i++) 79 for(int j=i+1;j<=n;j++) 80 if(a[i]<=a[j]&&g[i]+1==g[j]) 81 edge_add(i<<1|1,j<<1,1); 82 dinic(); 83 tot=0; 84 memset(fi,0,sizeof(fi)); 85 memset(to,0,sizeof(to)); 86 memset(next,0,sizeof(next)); 87 memset(rev,0,sizeof(rev)); 88 memset(cost,0,sizeof(cost)); 89 for(int i=1;i<=n;i++){ 90 edge_add(i<<1,i<<1|1,1); 91 if(g[i]==1)edge_add(1,i<<1,1); 92 if(g[i]==top)edge_add(i<<1|1,n*2+2,1); 93 } 94 for(int i=1;i<n;i++) 95 for(int j=i+1;j<=n;j++) 96 if(a[i]<=a[j]&&g[i]+1==g[j]) 97 edge_add(i<<1|1,j<<1,1); 98 edge_add(2,3,0x3fffffff);edge_add(n<<1,n<<1|1,0x3fffffff); 99 if(g[1]==1)edge_add(1,2,0x3fffffff); 100 if(g[n]==top)edge_add(n<<1,n*2+2,0x3fffffff); 101 dinic(); 102 return 0; 103 }View Code
[網絡流24題] 最長遞增子序列