Link
https://jzoj.net/senior/#main/show/3521
Description
Tar把一段凹凸不平的路分成了高度不同的N段,並用H[i]表示第i段高度。現在Tar一共有n種泥土可用,它們都能覆蓋給定的連續的k個部分。對於第i種泥土,它的價格為C[i],可以使得區間[i,min(n,i+k-1)] 的路段的高度增加E[i]。Tar要設定一種泥土使用計划,使得使用若干泥土后,這條路最低的高度盡量高,並且這個計划必須滿足以下兩點要求:
(1)每種泥土只能使用一次。
(2)泥土使用成本必須小於等於M。
請求出這個最低的高度最高是多少。
Solution
30分
事實上,數據是允許拿到90分的,我們直接跑一次遞歸就可以了,記錄最低值的最高值
100分
這個的狀壓DP很巧妙,從來沒做過這種不需要存所有狀態的狀壓DP
從“最低的高度最高”就可以看出,這道題是用二分的,正解就是二分答案。
從而,我們就把這道題目變為一道判定性的問題,給出一個mid,問你是否符合上面的題意。
普通方法使用遞歸判斷可行性
既然可以用遞歸,那么顯然可以用動態規划。
我們設F[i,s]表示你當前想選第i種泥土,前面k種的狀態(包括i)是什么,用二進制表示,,0表示在那個位置沒用了泥土,1表示用了。(用沒用表示從他開始往后鋪,專屬於他的泥土)一定要看下面第二個圖
我們用當前F[i,s]去更新F[i+1,s1]
關鍵是我們怎么轉移。
對於每一種泥土,只有選和不選兩種狀態,那么我們只要考慮這兩種情況就可以了
在此之前,我們先看一個東西
注意,我們這里是用i,更新i+1
讀圖可以發現,對i+1有影響的只有從i前k-1個(包括i)
可以發現,只有i包括i的前k-1個對i+1是有影響的。
因為你選到i時,狀態是s,那么,如果從i更新到i+1,狀態就是s去掉最后一位,也就是(s>>1),其實就是s/2
我們統計s狀態中,有哪個地方是選了泥土的,然后記錄一下,他們一共會讓i+1這個位置的土地高多少,設這個數為num,要多看圖,多寫草稿
為什么呢,因為前面的加了對應的e數組的某一個值,又因為它會影響到當前這個位置,看上圖,所以,我們要看看,他們究竟影響到i+1這個位置增加多少值,所以我們要提前記錄下來
其中S是枚舉的!枚舉的!枚舉的!
①不選
如果不選,那么num+h[i]必定是大於等於mid的,這樣才符合題目要求,如果不懂反復讀上面的那句話,看看我畫出來精美的圖,就知道了。不懂都會懂
滿足的上面的情況,我們就可以轉移了
f[i+1,s>>1]:=min(f[i+1,s>>1],f[i,s])
②選
如果選,就要滿足num+h[i]+E[i]是大於等於mid的,同理也是上面所說
那么當前的位置s狀態中i+1的位置應該是1,所以,我們就是要更新新的s
f[i+1,s>>1 or 1 shl (k-1)]:=min(f[i+1,s>>1 or 1 shl (k-1)],f[i,j]+c[i]);
最后答案的判斷就是判斷有沒有一個被更改過值得f[n,i](i是狀態)是小於等於m的,因為在前面的操作中,都保證了更新過的f數組是符合題目要求的
這道題充分的體現了動態規划的屌
Code
uses math; var n,m,k,i,j,l,r,mid:longint; a,b,c:array[0..100] of longint; f:array[0..100,0..2047] of longint; function pd(x:longint):boolean; var i,j,jj,num:longint; begin fillchar(f,sizeof(f),255); f[0,0]:=0; for i:=0 to n-1 do begin for j:=0 to 1 shl k-1 do if f[i,j]<>-1 then begin num:=0; for jj:=k-1 downto 1 do if (1 shl(jj)) and j<>0 then inc(num,b[i-(k-jj-1)]); if num+a[i+1]>=x then if f[i+1,j shr 1]<>-1 then f[i+1,j shr 1]:=min(f[i+1,j shr 1],f[i,j]) else f[i+1,j shr 1]:=f[i,j]; if num+a[i+1]+b[i+1]>=x then if f[i+1,j shr 1+1 shl (k-1)]<>-1 then f[i+1,j shr 1+1 shl (k-1)]:=min(f[i+1,j shr 1+1 shl (k-1)],f[i,j]+c[i+1]) else f[i+1,j shr 1+1 shl (k-1)]:=f[i,j]+c[i+1]; end; end; for i:=0 to 1 shl k-1 do if f[n,i]<>-1 then if f[n,i]<=m then exit(true); exit(false); end; begin assign(input,'cover.in');reset(input); assign(output,'cover.out');rewrite(output); readln(n,m,k); for i:=1 to n do readln(a[i],b[i],c[i]); l:=1; r:=maxlongint-1; while l<=r do begin mid:=(l+r) shr 1; if pd(mid) then l:=mid+1 else r:=mid-1; end; writeln(l-1); end.