這是一道動態規劃的dp問題
但是明顯需要o(n^3)的時間複雜度 ,顯然需要進行優化,
並且有明顯的可以進行單調佇列優化的特徵,在本次確定高度之後,
總能在前一次當中尋找到乙個最優解。
最優解當然是由上一次的積累量+本次積累量(其中上一次的積累量與本次的積累量之間無關聯)
就是說dp[i-1][k]的積累量只是與k有關 dp[i][j]的積累量至於j有關
由本次狀態轉移方程
dp[i][j]=min(dp[i-1][k] + abs(j-k)*c + (x[i]-j)*(x[i]-j));其中x[i]是第i個兒子原本的身高
通過分解滿足條件
下面摘抄一位大牛的部落格
單調佇列是一種嚴格單調的佇列,可以單調遞增,也可以單調遞減。隊首位置儲存的是最優解,第二個位置儲存的是次優解,ect。。。
單調佇列可以有兩個操作:
1、插入乙個新的元素,該元素從隊尾開始向隊首進行搜尋,找到合適的位置插入之,如果該位置原本有元素,則替換它。
2、在過程中從隊首刪除不符合當前要求的元素。
單調佇列實現起來可簡單,可複雜。簡單的乙個陣列,乙個head,乙個tail指標就搞定。複雜的用雙向鍊錶實現。
用處:1、儲存最優解,次優解,ect。
2、利用單調佇列對dp方程進行優化,可將o(n)複雜度降至o(1)。也就是說,將原本會超時的n維dp降優化至n-1維,以求通過。這也是我想記錄的重點
是不是任何dp都可以利用單調佇列進行優化呢?答案是否定的。
記住!只有形如 dp[i]=max/min (f[k]) + g[i] (k優化的物件就是f[k]。
通過例題來加深感受
我要長高
韓父有n個兒子,分別是韓一,韓二…韓n。由於韓家演技功底深厚,加上他們間的密切配合,演出獲得了巨大成功,票房甚至高達2000萬。舟子是名很有威望的公知,可是他表面上兩袖清風實則內心陰暗,看到韓家紅紅火火,嫉妒心遂起,便發微薄調侃韓二們站成一列時身高參差不齊。由於舟子的影響力,隨口一句便會造成韓家的巨大損失,具體虧損是這樣計算的,韓一,韓二…韓n站成一排,損失即為c*(韓i與韓i+1的高度差(1<=i有若干組資料,一直處理到檔案結束。 每組資料第一行為兩個整數:韓子數量n(1<=n<=50000)和舟子係數c(1<=c<=100) 接下來n行分別是韓i的高度(1<=hi<=100)。
首先建立方程,很容易想到的是,dp[i][j]表示第 i 個兒子身高為 j 的最低花費。分析題目很容易知道,當前兒子的身高花費只由前乙個兒子影響。因此,
dp[i][j]=min(dp[i-1][k] + abs(j-k)*c + (x[i]-j)*(x[i]-j));其中x[i]是第i個兒子原本的身高
我們分析一下複雜度。
首先有n個兒子,這需要乙個迴圈。再者,每個兒子有0到100的身高,這也需要一維。再再者,0到100的每乙個身高都可以有前一位兒子的身高0到100遞推而來。
所以樸素演算法的時間複雜度是o(n^3)。題目只給兩秒,難以接受!
分析方程:
當第 i 個兒子的身高比第 i-1 個兒子的身高要高時,
dp[i][j]=min(dp[i-1][k] + j*c-k*c + x); ( k<=j ) 其中 x=(x[i]-j)*(x[i]-j)。
當第 i 個兒子的身高比第 i-1 個兒子的身高要矮時,
dp[i][j]=min(dp[i-1][k] - j*c+k*c + x); ( k>=j )
對第乙個個方程,我們令 f[i-1][k]=dp[i-1][k]-k*c, g[i][j]=j*c+x; 於是 dp[i][j] = min (f[i-1][k])+ g[i][j]。轉化成這樣的形式,我們就可以用單調佇列進行優化了。
第二個方程同理。
接下來便是如何實現,實現起來有點技巧。具體見下
1 #include2 #include下面是我重寫的**感觸頗多3 #include4 #include5
using
namespace
std;
6#define inf 0xfffffff
7#define min(a,b) a8
#define max(a,b) a>b?a:b910
int dp[2][101
];11
intn,c;
12int q[101
];13
inthead,tail,cur;
1415
intmain()
1644
//比前乙個人矮
45 head=tail=0
; 46
for(j=100;j>=0;j--) //
當身高為j時候,佇列裡便已經儲存了100~j+1的資訊,正寫反寫是有技巧的
4755}56
int ans=inf;
57for(i=0;i<=100;i++)
58 ans=min(ans,dp[cur][i]);
59 printf("
%d\n
",ans);60}
61return0;
62 }
#include#define inf 0xfffffff
int n,c,flag,a,i,j,front,rear,temp;
int que[100];
int dp[2][110];//用於記錄最優解只儲存最後結果
int main()}}
int ans = inf;
for(i=0;i<=100;i++)
printf("%d\n",ans);
}return 0;
}
首先是在inf的訪問上來說,一開始把7個f看成了8個f造成了不理解,明明是取乙個很大的值,怎麼變成了乙個-1呢?
隨後自己取了0x7fffffff這個值,結果出現了錯誤,原因是在過程中會進行加法,這個最大的int整形,加上乙個數字之後就變成了
負數了。於是就得出了十分錯誤的結果。
還有就是在起初初始化的時候對小於a的值進行inf賦值是很有必要性的,因為其實每個值都會用得上的,都會進行計算入隊的
我們只是取得最小值(符合條件的)
下面這個解法是我從另乙個部落格摘取的 效率更高
其實我們根本沒有必要去維護乙個佇列,其實只需要要在高於或者矮於的情況下的最小值就行了,於是乎在
佇列維護的時候只是需要一次最小值比較就行了
還有在高於前乙個人的情況下只是需要從h【i-1】開始列舉到mlen就行了
在矮於前乙個人的身高的時候 只需要從mlen 到 h【i】列舉就行了
很好吧
#include #include #include #include #define inf 0x3fffffff
#define maxn 50005
using namespace std;
int n, m, h[maxn], dp[maxn][105];
// dp[i][j] 第i個人身高為j時的最少代價
// 當前面的身高小於其身高時
// dp[i][j] = min( dp[i-1][k] + mj - mk + (j - h[i])^2 )同理
// 對於上面的式子我們只需要維護好上一次狀態的 dp[i-1][k]-mk 的最小值就可以了
// dp[i][j] = min( dp[i-1][k] + mk - mj + (j - h[i])^2 );
// 對於上面的式子我們只需要維護好上一次狀態的 dp[i-1][k]+mk 的最小值就可以了
int main()
for (int i = 0; i <= n; ++i)
for (int i = h[1]; i <= lim; ++i)
for (int i = 2; i <= n; ++i)
}min = inf;
for (int j = lim; j >= h[i]; --j) }}
min = inf;
for (int i = h[n]; i <= lim; ++i)
printf("%d\n", min);
}return 0;
}
UESTC 1685 我要長高
單調佇列優化dp。借鑑 首先建立方程,很容易想到的是,dp i j 表示第 i 個兒子身高為 j 的最低花費。分析題目很容易知道,當前兒子的身高花費只由前乙個兒子影響。因此,dp i j min dp i 1 k abs j k c x i j x i j 其中x i 是第i個兒子原本的身高 我們分...
UESTC 我要長高
題意是 就是題目描述的那樣了吧 題意很顯而易見,一眼dp題 dp i j 代表第i個人身高為j時的最小消耗,dp i j min dp i 1 k abs j k c j h i 2 複雜度o n h i 2 很明顯有點大,學習了一下單調佇列優化dp之後,發現形如dp i min max dp j ...
UESTC 我要長高 DP優化
韓父有n個兒子,分別是韓一,韓二 韓n。由於韓家演技功底深厚,加上他們間的密切配合,演出獲得了巨大成功,票房甚至高達2000萬。舟子是名很有威望的公知,可是他表面上兩袖清風實則內心陰暗,看到韓家紅紅火火,嫉妒心遂起,便發微薄調侃韓二們站成一列時身高參差不齊。由於舟子的影響力,隨口一句便會造成韓家的巨...