網路流24題 最長不下降子串行問題

2022-05-31 04:06:13 字數 3463 閱讀 7400

傳送門:>here

<

題意:

給定正整數序列$x_1,...,x_n$

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

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

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

思路分析

題意首先就很坑:注意第二問中的取出二字,意味著乙個數字最多只能存在於乙個lis中。所以才會有第三問的假設

第一問

第一問很簡單,直接暴力$o(n^2)$就好了

後面的兩問需要借助於網路流。

第二問

直接解釋原理不清晰也不易懂,先

詳細闡述如何建模過程:

首先原序列做lis,其中$f[i]$表示以$a[i]$為結尾的lis的最大長度。對序列的每乙個數進行拆點,也就是分為$$。我們要讓s到t的每一條路徑都是乙個lis,因此若$f[i]=1$,則$x_i$的點連線源點(容量為1,後同),$f[i]=maxs$則$y_i$連線匯點。其中對於每乙個數字,連線有向邊$(x_i, y_i)$。並且在做lis的過程中,若發現一對數字滿足$a[j]<=a[i] 且 f[j]+1=f[i]$,則連有向邊$(y_j, x_i)$。最後做最大流即可

我們發現這道題又和二分圖有一點類似。經過剛才這樣的連邊之後,任一s到t的路徑都是乙個lis,並且容量為1保證了每個點不會被用兩次(與二分圖原理相同),因此最大流即為方案數

至於與二分圖不同的地方我們發現,我們每個點本身連了邊,最後lis的邊還是反過來加的。為什麼要這麼做呢?這是這道題最大的難點。

首先分析這種方法的正確性:經過現在這樣的建圖,y部分稱為了邊的起點,而x成為了終點。設有一條邊是$(y_a, x_b)$,則其到達$x_b$以後會順著自己的邊到達$y_b$,而後如果還存在一條邊$(y_b, x_c)$,它就會繼續來到$y_c$,以此類推最終到達t。其中$(x_i, y_i)$起到了過渡作用,並且保證了每個點只走一次

然後來講這種方法的必要性:為什麼正著連邊不行呢?原因在於我們源點與匯點的選擇。想象乙個序列$5,4,3,2,1$,按照目前這種方法自然會跑出最大流為5(每個點直接沿著自己的邊走了)。然而如果正著連邊自己反著連,那麼將會跑出0——我們並沒有給它乙個跑出去的機會

第三問

第三問其實就是在第二問的基礎上稍作了一些改動。$x_1和x_n$可以用無限次意味著可以去掉之前給這兩個點只能用一次的束縛,因此只需要把源點到1,n到匯點的邊的容量改為inf即可。注意由於現在起點是右側點了,所以$(x_1,y_1)$和$(x_n,y_n)$的容量務必也要改成inf

code

細節題。dinic不要打錯了不然完蛋

另外,再dfs的時候由於我們最初給s設的可增廣量為inf,而容量也是inf,這就不太對了,因為源點還有可能連更多的邊,inf就不夠了。因此第三問的時候容量改為inf/10保險一點

/*

by dennyqi

*/#include

#include

#include

#include

#define r read()

#define max(a,b) (((a)>(b)) ? (a) : (b))

#define min(a,b) (((a)<(b)) ? (a) : (b))

using

namespace

std;

typedef

long

long

ll;const

int maxn = 10010

;const

int maxm = 10010

;const

int inf = 1061109567

;inline

intread()

intk,n,m,s,t,x,p,maxs;

int first[maxm*2],nxt[maxm*2],to[maxm*2],cap[maxm*2],flow[maxm*2],num_edge=-1

;int

level[maxn],cur[maxn];

queue

q;inta[maxn],f[maxn];

inline

void add(int u, int v, int c, int

f)inline

bool

bfs()}}

return level[t] != 0;}

int dfs(int u, int

a) }

return

ans;

}inline

void

dinic()

printf(

"%d\n

", ans);

}int

main()

maxs = 0

;

for(int i = 1; i <= n; ++i)

}++f[i];

maxs =max(maxs, f[i]);

for(int j = 1; j < i; ++j)}}

printf(

"%d\n

", maxs);

for(int i = 1; i <= n; ++i)

if(f[i] ==maxs)

}dinic();

memset(flow,

0, sizeof

(flow));

memset(first, -1, sizeof

(first));

memset(nxt,

0, sizeof

(nxt));

memset(f,

0, sizeof

(f));

s = 1, t = 2*n+2; num_edge = -1

;

for(int i = 1; i <= n; ++i)

add(i

<<1, i<<1|1, 1, 0

); add(i

<<1|1, i<<1, 0, 0

); }

maxs = 0

;

for(int i = 1; i <= n; ++i)

}++f[i];

maxs =max(maxs, f[i]);

for(int j = 1; j < i; ++j)}}

for(int i = 1; i <= n; ++i)

else

}if(f[i] ==maxs)

else

; }

}dinic();

return0;

}

網路流24題之最長不下降子串行

對於第一問直接n 2dp計算 第二問建圖跑網路流 第三問將起始與結尾流量開大 建邊的時候要嚴格按照子串行求法建 by 大奕哥 1 include2 using namespace std 3const int n 10005 4 inthead n d n f n a n 5int n,m,cnt ...

網路流24題 最長不下降子串行問題

luogu 2766 最長不下降子串行問題 傳送門第一問 o n 2 的dp求lis 為了下面敘述方便,我們將dp過程講一遍 子狀態 dp i 表示以a i 結尾的lis長度 初始條件 dp i 1 狀態轉移方程 dp i dp j 1 j 第二問 我們發現若a j 加上a i 可以構成乙個不下降子...

網路流24題 最長不下降子串行問題(最大流)

題目鏈結 這個題目有三個要求出來的 計算其最長不下降子串行的長度s。計算從給定的序列中最多可取出多少個長度為s的不下降子串行 如果允許在取出的序列中多次使用x1和xn,則從給定序列中最多可取出多少個長度為s的不下降子串行 自己的想法 對於 問題1 直接用那個動態規劃去跑下,就求出答案len了,這是會...