您的位置:首页 > 其它

URAL 1776 Anniversary Firework (概率,区间DP)

2015-08-13 22:02 302 查看
坑,一开始以为,分成两半的时候去最大那个就行了,

实际上这样是不对的,因为有可能出现小的一半的时间比大的要长,

因为还和等待次数有关,且转移的时候需要用到次数更小的状态,

所以状态定义为二维,dp[i][j]表示长度为i的区间,放小于等于j次的概率。

要求确切的某次的概率,比如k,就只要用dp[i][k]-dp[i][k-1]就行了。

如何转移?从小到大枚举i,从小到大枚举j,初始化dp[i][j] = dp[i][j-1],

然后求出确切等待j次的概率,以k为界限划分区间,分成l,r两段,加上l区间等待j-1次且r区间等待小于等于j-1次的概率,

类似得加上r区间等待j-1次且l区间等带小于等于j-1次的概率,然后减掉重复计算的状态。

因为只要求中间等待的次数,且一开始要放两个鞭炮,所以可以等效为一开始不计等待,之后每次都计算等待时间。

还有一个细节是每次j从2开始枚举,放一次的只可能是长度为1的情况。

g++使用%lf正常,但在有些oj却会出问题

#include<bits/stdc++.h>
using namespace std;

const int maxn = 402;

double dp[maxn][maxn];

int main()
{
//freopen("in.txt","r",stdin);
int n; scanf("%d",&n);
n -= 2;

for(int i = 0; i <= n; i++){
for(int j = i; j <=n; j++)
dp[i][j] = 1;
}

for(int i = 3; i <= n; i++){
double e = 1./i;
for(int j = 2; j < i; j++){
dp[i][j] = dp[i][j-1];
for(int k = 1; k <= i; k++){
int l = k-1,r = i-k;
double p1 = dp[l][j-1] - dp[l][j-2], p2 = dp[r][j-1] -  dp[r][j-2];
double p3 = dp[l][j-1];
double p4 = dp[r][j-1];
dp[i][j] += e*(p1*p4 + p2*p3 - p2*p1);
}
}
}
double ans = 0;
for(int i = 1; i <= n; i++){
ans += (dp
[i]-dp
[i-1])*i*10;
}
printf("%.11lf\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: