D e s c r i p t i o n Description Description
牛牛在玩飞行棋。
有无限个格子排成一行,从左到右,标号为 0,1,…,n,…,终点为 0,有一架飞机一开始在 n 号位置。
排骨龙每回合可以先投掷一次 d 面的骰子,1 到 d 等概率出现。
1.投出点数 x 后,飞机会移动 x 步,每步移动一格,方向初始向左移动,若到达终点,会向右移动。
2.若投出的点数为 d 点,可以继续投掷,直到投出的点数不是 d 点。
求让这架飞机停在终点回合数的期望。
I n p u t Input Input
第一行一个数字 T 表示 T(T ≤ 100) 组数据。
接下来每行两个正整数 n,d(2 ≤ d, n ≤ 100000)
对于 20% 数据,2 ≤ n, d <=15
对于 50% 数据,2 ≤ n, d ≤ 150
对于 100% 数据,2 ≤ n, d ≤ 100000
O u t p u t Output Output
输出 T 行,每行保留两位小数输出答案。
S a m p l e Sample Sample I n p u t Input Input
6
1 6
2 6
3 6
4 6
5 6
6 6
S a m p l e Sample Sample O u t p u t Output Output
5.00
5.00
5.00
5.00
5.00
5.17
T r a i n Train Train o f of of T h o u g h t Thought Thought
先分类讨论
当 n < d 时与当 n >= d
当 n < d 时,那么你丢出的筛子就有d种可能
但是因为当丢到d时重丢
所以实际上是只有d - 1种可能
因为只要丢到某一个数恰好走到0
否则就要继续丢
所以设ans步到终点,则
解得ans = d - 1
然后当n >= d的时候
就直接DP
但是O(Tnd)它炸了
所以得优化
把左边部分化简,用前缀和来算,就是
嗯可以了
这个是优化前(50分
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
double F[100250];
int T, n, d;
int main()
{
scanf("%d", &T);
while(T--)
{
memset(F, 0, sizeof(F));
scanf("%d%d", &n, &d);
if(n < d)
{
printf("%d.00\n", d - 1);
continue;
}
F[0] = 1;//为了方便计算
for(int k = 1; k < d; ++k)
F[k] = d - 1;
for(int k = d; k <= n; ++k)
{
for(int i = k - d + 1; i < k; i++)
F[k] += (F[i] + 1) / (d * 1.0);
F[k] += F[k - d] / (d * 1.0);
}
printf("%.2lf\n", F[n]);
}
return 0;
}
优化后(满分
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
double F[100250], Sum[100250];
int T, n, d;
int main()
{
scanf("%d", &T);
while(T--)
{
memset(F, 0, sizeof(F));
memset(Sum, 0, sizeof(Sum));
scanf("%d%d", &n, &d);
if(n < d)
{
printf("%d.00\n", d - 1);
continue;
}
F[0] = Sum[0] = 1;//方便计算
for(int i = 1; i < d; ++i)
F[i] = d - 1, Sum[i] = Sum[i - 1] + F[i];
for(int i = d; i <= n; ++i)
{
F[i] = (Sum[i - 1] - Sum[i - d] + d - 1) / (d * 1.0);
F[i] += F[i - d] / (d * 1.0);
Sum[i] = Sum[i - 1] + F[i];
}
printf("%.2lf\n", F[n]);
}
return 0;
}