標籤:style http color 使用 os io for ar
UVA 10529 - Dumb Bones
題目連結
題意:你試圖把一些多米諾骨牌排成直線,然後推倒它們。但是如果你在放骨牌的時候不小心把剛放的骨牌碰倒了,它就會把相臨的一串骨牌全都碰倒,而你的工作也被部分的破壞了。 比如你已經把骨牌擺成了DD__DxDDD_D的形狀,而想要在x這個位置再放一塊骨牌。它可能會把左邊的一塊骨牌或右邊的三塊骨牌碰倒,而你將不得不重新擺放這些骨牌。 這種失誤是無法避免的,但是你可以應用一種特殊的放骨牌方法來使骨牌更多的向一個方向倒下。 給出你要擺放的骨牌數目,以及放骨牌時它向左和向右倒的機率,計算你為完成任務擺放的骨牌數目的平均數。假設你使用了最佳的擺放策略。 輸入將包含至多100個測試點,每個測試點佔一行,包含需要擺放的骨牌數目n (1≤n≤1000),以及兩個非負實數Pl, Pr,表示骨牌向左和向右倒的機率。保證1<Pl+Pr≤0.5。 最後一個測試點包含一個數0。對於每個測試點輸出題目要求的數目,保留兩位小數。
思路:機率,+區間dp,dp[i]表示有i個連續的多米諾骨牌,那麼每次要組成i,就可以選中間任意一個位置,把這個骨牌分成兩部分,dp[l]和dp[r]然後考慮在放一個,如果碰到左邊,就要重新放左邊,右邊同理,根據期望公式,平均1 / (1 - pl - pr)步能成功放一個,也就是說之前都會有碰倒,那麼碰倒的話需要走的步數期望為(1 + dp[l]pl + dp[r] pr),所以期望為(1 + dp[l] pl + dp[r] pr) / (1 - pl - pr) + dp[l] + dp[r],狀態轉移方程為
dp[i] = min(計算機率(dp[l], dp[r])) {枚舉中間位置求出l, r}
於是這題遞推求解就能過了,不過複雜度是O(n^2),其實還有可以最佳化的地方
可以根據動態規劃時候,dp[i]這個數組在找尋最小值的時候,其實方程是滿足一個下凹函數的,所以這步實際上可以利用三分求解,複雜度為O(nlog(n)),然後實際上,對於下凹函數,那麼其實對於下次找最小值的位置,是不會減小的,因此如果每次維護記錄下上次找到答案的位置,這樣均攤下來,時間複雜度就能最佳化到O(n)
代碼:
#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define INF 0x3f3f3f3fconst int N = 1005;int n;double p, pl, pr, dp[N];double solve() { p = 1 - pl - pr; dp[0] = 0; dp[1] = 1 / p; int pre = 0; for (int i = 2; i <= n; i++) {dp[i] = INF;for (int j = pre; j < i; j++) { int l = j, r = i - 1 - j; double tmp = dp[l] + dp[r] + (pl * dp[l] + pr * dp[r] + 1) / p; if (dp[i] > tmp) {dp[i] = tmp;pre = j; }} } return dp[n];}int main() { while (~scanf("%d", &n) && n) {scanf("%lf%lf", &pl, &pr);printf("%.2lf\n", solve()); } return 0;}