求乙個最長不下降子串行,以及這個最長不下降子串行在這個序列裡的個數(不能重複)。
對於第一問我們就只要普通的dp一下就行了,(以下將最長不下降子串行稱為最長序列)對於第二問這裡要詳細的講一下。
狀態有如下:
f[i]表示到第i個數的最長序列;
b[i]表示到第i個數的最長序列的個數;
很容易得出方程:
f[i]:=max; b[i]:=max(1<=j<=i-1,且f[j]+1=f[i]);
但是b[i]的狀態僅僅這樣就可以了嗎?
對於如下的乙個序列: 5
69 68 64 67 62
對於求最長序列我們很簡單的知道是4.那麼有多少個最長序列呢?
69 68 64 62
69 68 67 62
顯然有兩個。
那這兩個是如何得出的呢?
我們可以先注意到前4個數在b陣列裡都是1,只有第5個是2,這是為什麼呢?
先看一下陣列:
f=1 2 3 3 4
b=1 1 1 1 2
其實f[5]是由f[4]得來的(j是逆推的,從4~1),然後進行到j=3的時候,f[3]+1=f[5],也就是說f[5]也可以由f[3]得來,所以這裡就需要把b[i]加上b[j]了(簡稱為work)。
然後當把f[i]更新的時候,一定要把b[i]更新,因為你現在是以乙個新的序列結尾了,之前的一律不算了。
再舉乙個栗子: 5
69 68 67 67 62
這裡如果按照上面的演算法,則最長序列的個數為2.實際上因為題目說了不能重複最長序列,則指在i=5的時候,j=3不能加上b[j]。那這如何解決呢?——可以把當前f[i]是由哪乙個得來的標記一下,例如i=5是由j=4得來的,則a[4]——第4個數標為false,如果再遇到與a[4]相同的如a[3]就不能work了。
所以b[i]的正確狀態是:
b[i]:=max(1<=j<=i-1,且f[j]+1=f[i],bz[a[j]]);
結束後就是輸出了,如何輸出呢?這裡有乙個巧妙的處理,就是把i的取值擴到1,變為n+1,而a[n+1]=0,然後輸出f[n+1]-1,b[n+1],這有什麼好處呢?
其一:輸出f[n+1]-1這個很容易理解,這就不用再o(n)找一便最大的了,f[n+1]一定就是最大的+1,減1就是最長序列了。
其二:這裡輸出b[n+1]有什麼用呢?——我們再看乙個栗子: 8
1 2 3 4 5 6 7 8
f,b陣列的狀態如下:
f=1 1 1 1 1 1 1 1
b=1 1 1 1 1 1 1 1
輸出:1 8
b陣列的前8個都是1,可實際上最長序列的個數卻是8,但因為始終沒有滿足b陣列work的條件,所以如果這裡再算乙個n+1的話,則f[9]=2,然後在j=8的時候就會讓b陣列work了。最後就會剩下7個累加進去,1+7=8,則最後的b[n+1]就等於8了。
這樣有什麼好處?顯而易見了,這樣就可以把每乙個可能相等的最長序列做乙個最終的合併,可以得到正確的答案了、
var
i,j,k,n:longint;
a,b,f:array[0..5000] of qword;
bz:array[0..50000] of boolean;
begin
readln(n);
for i:=1
to n do
read(a[i]);//輸入
f[1]:=1;
b[1]:=1;//初始值設定
for i:=2
to n+1
dobegin
f[i]:=1;
b[i]:=1;//初值
for j:=i-1
downto1do
if (a[i]then
if f[j]+1>f[i] then//判斷當前是否可以組成「更」長的序列
begin
f[i]:=f[j]+1;
fillchar(bz,sizeof(bz),true);//這裡更新bz陣列因為新的乙個最長序列必須更新以保證bz陣列的不變,因為bz=true表示的是以相等的數時,這個位置更優,而當新的序列更新的時候前面的所有更優位置都需要重新更新。
bz[a[j]]:=false;
b[i]:=b[j];
endelse
if (f[j]+1=f[i]) and bz[a[j]] then //當這兩個條件滿足時,則這個方案既沒重複,又需累加。
begin
bz[a[j]]:=false;//這也是非常重要的更新,加上這個才能避免重複計算b[j]
b[i]:=b[i]+b[j];
end;
end;
writeln(f[n+1]-1,' ',b[n+1]);
end.
總結:這一題目比較難,而且方法不唯一,需要好好消化。
編輯距離(動規例題)
題目描述 現有字串a,b,要使得a b,有如下三 種操作 將a刪除1個字元 將a插入1個字元 將a中的乙個字元改為另乙個字元 樣例輸入 sfdqxbw gfdgw 樣例輸出 4 解釋 sfdqxbw gfdqxbw gfdxbw gfdbw gfdgw,四步。這道題因為只能操作a,就能有效避免動態規...
初學動規 例題 滑雪
本人是根據mooc大學郭煒老師得程式設計與演算法進行的個人總結 一般思路 1 把原問題轉化為若干的子問題,子問題和原問題形式相同或類似,只是規模變小了。子問題的解一旦求出 就把他儲存下來,如果再遇到就可以直接使用無需再進行計算。3 確定初始狀態 邊界狀態 的值 4 狀態轉移方程。從乙個或多個值已知狀...
方格取數(動規例題)
對於n n的乙個矩陣中有著許多數字,規定你從左上角出發,走到右下角,走兩遍,每一次走時可以取矩陣中的數字,求如何取得最大的和。這道題可以四重迴圈列舉兩條路所走到的位置。然後判斷i,j點,h,k點是由上或左得來最大值,sum i,j,h,k 表示第一條道路走到i,j點,第二條走到h,k點時的最優值,顯...