codeforces 1101F Trucks and Cities 區間dp+單調優化 好題
題目傳送門
題意簡述:(來自洛谷)
有n個城市坐落在一條數軸上,第ii個城市位於位置ai?.
城市之間有m輛卡車穿行.每輛卡車有四個參數:si?為起點編號,fi?為終點編號,ci?表示每行駛1個單位長度需要消耗的油量,ri?表示可以在路途中加油的次數.
當卡車到達一個城市的時候可以將油加滿(當然也可以不加),在路中無法加油,但是路途中總加油次數不能超過ri?.
所有卡車的油箱都是一樣大的,我們稱它的容積為V.試求一個最小的V,使得對於所有的卡車都存在一種方案,在路途中任意時刻油箱內的油量大於等於0且路途中總加油次數小於等於ri?的情況下從起點城市到達終點城市.
n,m(n≤400,m≤250000)表示城市數量與卡車數量。
思路:
此題學習了洛谷的博客,但洛谷的博客有地方是錯誤的,導致自閉了許久,自己證明了一波,才走出自閉。
洛谷題解 點這裏 但是洛谷題解有錯,並且最重要的單調性沒有證明。
首先,主體是一個區間DP
設 dp{i,j,k}? 為:從第 i 個城市到第 j 個城市分成 k 段,這 k 段中長度最大的一段的最小值
邊界: dp{i,j,0}=aj-ai(1≤i≤j≤n)。
狀態轉移方程
dp {i,j,k}=dp{i,j,k}?=min(? (max(dp{i,w,k?1}?,aj??aw?))(0<k≤n))( i <= w <= j )
目標:max?(ci*?dp{si?,fi?,ri?}?) i<=m
以上均取自洛谷,並且洛谷的狀態轉移方程還寫錯了。上面這個區間dp的時間復雜度是O(n4)的,顯然會超時,要進行優化,洛谷題解中說單調性是顯然得出的,,然而我證明了好久。
先說兩個結果:
1)當 k,i 確定, j 在不斷向右移時,對每個 j 取到的 w 具有單調性。
2)同時,當 j 確定時,不同的 w 對應的取值呈“先減後增” 的趨勢,
我們先證明第二點,當i j k 確定時,轉移方程中 我們設dp{i,w,k?1}?為 Aw,aj??aw? 為Bw,當w變大時,Aw可能變大,Bw必定變小,所以取值一開始肯定是取Bw的,慢慢變的有可能取Aw,可以想象,這個dp方程一開始肯定是w越大越好,但是當某一個臨界點,如果比前面大了,那我們會發現,此時的最大值必定不是Bw,因為Bw<B(w-1),這是必定的。所以最大值是Aw,而w越大,Aw則可能變大,但絕不變小,所以不會有變小的趨勢了,證畢。
然後證明第一點,假設j‘=j+1,我們先看w是否會前移。發現j變成j‘後,只有Bw會變大,Aw是不變的,而越往後的項,最大值取Aw的可能性越大,所以前面的項只會變大,就算當前項變大了,那變大的程度也會和前面的一樣,所以取最小值的話當前w必定由於小於w的值。
那麽看w是否會後移,由於Bw會變大,Aw只是可能變大,後面的項比前面的項更有可能用到Aw,所以後面的項可能更優,w可能後移,有單調性,證畢。
註意不要開數組不要long long,會爆內存,也不要開太大,開了410*410*410會mle。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=402; ll ans; int dp[maxn][maxn][maxn],a[maxn]; int n,m; int main(){ while(cin>>n>>m) { ans=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { dp[i][j][0]=a[j]-a[i]; } } for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { int w=i; for(int j=i;j<=n;j++) { while(w<j&&max(dp[i][w][k-1],a[j]-a[w])>max(dp[i][w+1][k-1],a[j]-a[w+1]))w++; dp[i][j][k]=max(dp[i][w][k-1],a[j]-a[w]); } } } int s,f,r; ll c; while(m--) { scanf("%d%d%lld%d",&s,&f,&c,&r); ans=max(ans,dp[s][f][r]*c); } cout<<ans<<endl; } }View Code
codeforces 1101F Trucks and Cities 區間dp+單調優化 好題