UVA 10529 - Dumb Bones(概率+区间dp)

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 0x3f3f3f3f
const 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;
}

UVA 10529 - Dumb Bones(概率+区间dp),布布扣,bubuko.com

时间: 2024-10-22 22:12:04

UVA 10529 - Dumb Bones(概率+区间dp)的相关文章

uva 10529 - Dumb Bones(概率+区间dp)

题目连接:uva 10529 - Dumb Bones 题目大意:给定n,表示要放n个骨牌,每次放下骨牌,有可能向左倒的概率为pl,向右倒的概率为pr,如果倒下,会将那一侧的骨牌全部推倒,可以选择位置先后放骨牌,问说一种放骨牌次数最少的期望是多少. 解题思路:dp[i]表示放i个骨牌需要的步数期望,维护一个最优放的位置,dp[i] = min\{ (从i-1到i的步数)} + (0到i-1的步数)} (从i-1到i的步数):dp[i?j?1]?pl+dp[j]?pr+11?pl?pr (0到i-

UVA 10529 Dumb Bones 概率dp 求期望

题目链接:点击打开链接 题意: 要在一条直线上摆多米诺骨牌. 输入n, l, r 要摆n张排,每次摆下去向左倒的概率是l, 向右倒的概率是r 可以采取最优策略,即可以中间放一段,然后左右两边放一段等,摆放顺序任意. 问:在最佳策略下要摆成n张牌的期望次数. 思路: 点击打开链接 #include <cstdio> #include <iostream> #include <cstring> #include <queue> #include <algo

UVA 10529 - Dumb Bones (概率dp)

题目描述 You are trying to set up a straight line of dominos, standing on end, to be pushed over later for your entertainment. (Sure, it seems pointless to set something up only to knock it down again, but you have some strange hobbies) The tricky thing

#11 UVA 10529 Dumb Bones

题意: 放一堆排,每放一张,有pa的概率让左边的全倒,有pb的概率让右边全倒 问在最优策略下,最少要放几张才能摆放出n张 1<=n<=1000 题解: 这题应该还是很经典的 首先是期望部分 我们通过枚举最后一步,来分割序列 很容易知道中间的点应该要放1/(1-pa-pb)次 那么左边倒的次数就是pa/(1-pa-pb)次 这样dp方程就很简单了 dp[i]=min((dp[ls]*pa+dp[rs]*pb)/(1-pa-pb)+dp[ls]+dp[rs]) 发现朴素的是n^2的 性质1: 单峰

UVA 10529 Dumb Bones

#include <iostream> #include <stdio.h> #include <cstring> #define MAX 1010 using namespace std; double pl,pr; double data[MAX]; void Init() { memset(data, 0, sizeof(data)); } double dp(int n) { double temp; int l,r; if(n==0) return 0; if

uva live 3516 Exploring Pyramids 区间DP

// uva live 3516 Exploring Pyramids 区间DP // // 题目大意: // // 给你一个多叉树,每个节点是一个大写字母,从根节点走,按照先序遍历的 // 原则访问,不能访问则回溯,每次记录一下节点的字符,最后得到一个字符串.现 // 在给你一个字符串,问可能符合条件的多叉树的数量. // // 解题思路: // // 区间DP,我们注意到,从根节点出发,一定会再次回到根节点,那么我们可以设 // d(i,j) 是序列i到j段形成的符合条件的多叉树的数量,则

uva 10003 Cutting Sticks 简单区间dp

// uva 10003 Cutting Sticks 区间dp // 经典的区间dp // dp(i,j)表示切割小木棍i-j所需要的最小花费 // 则状态转移为dp(i,j) = min{dp(i,k) + dp(k,j) + a[j]-a[i]) // 其中k>i && k<j // a[j] - a[i] 为第一刀切割的代价 // a[0] = 0,a[n+1] = L; // dp数组初始化的时候dp[i][i+1]的值为 0,这表示 // 每一段都已经是切割了的,不

uva 10003 Cutting Sticks 【区间dp】

题目:uva 10003 Cutting Sticks 题意:给出一根长度 l 的木棍,要截断从某些点,然后截断的花费是当前木棍的长度,求总的最小花费? 分析:典型的区间dp,其实和石子归并是一样的,花费就是石子的和,那么久不用多说了. AC代码: #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include <map> #include <

UVA 10891 Game of Sum 区间dp

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19461 题目意思大致是给你一串数字,A,B两个人轮流从两端取一段数字并得到该串数字的和的点数,每个人都尽可能的多的点数,问A最多能比B多多少点. 区间dp,一开始打算分AB,但是发现太麻烦了,最后用dp(l,r)表示在区间l~r中先手能赢的的最多点数.假设A是区间(l,r)的先手的话,如果A选择了(l,k )// 或(k+1,r)的数字,那他的得分(l,r)的总分减去B在余