https://vjudge.net/contest/422848

A - A Dangerous Maze

题意

给若干个门,每次从中随机选取一个门,每个门对应有一个数值$x_i$.若$x_i>0$,则你将在$x_i$分钟后离开迷宫,反之你将在$x_i$分钟后回到原地。现在,你每次闲置(没有等待某个$x_i$时)时都会随机选一个门进入。问离开迷宫的时间期望。

分析

列公式。

设期望为$E$,则其为离开迷宫的时间期望+选取$x_i<0$的门浪费掉的时间期望。

设n~1~是$x_i>0$的门个数,n~2~是$x_i<0$的门的个数。

先直接得出$E=\frac{\sum{(x_i>0?x_i:0)}}{n_1}×\frac{n_1}{n} + (E + \frac{\sum{(x_i<0?x_i:0)}}{n_2})×\frac{n_2}{n}$.

化简得到$E=\frac{\sum abs(x_i)}{n_1}$.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
void done(int& s1, int& s2)
{
int u = __gcd(s1, s2);
s1 /= u, s2 /= u;
}
int main()
{
int t;
scanf("%d", &t);
int kase = 1;
while(t--)
{
int n; scanf("%d", &n);
int a[n];
int s1 = 0, n1 = 0, n2 = 0;
for(int i = 0; i < n; ++i){
scanf("%d", &a[i]);
if(a[i] > 0) n1++;
s1 += abs(a[i]);
}
if(n1){
done(s1, n1);
printf("Case %d: %d/%d\n", kase++, s1, n1);
}
else printf("Case %d: inf\n", kase++);
}
return 0;
}

B - Discovering Gold

分析

尝试期望dp。思路是设$dp[i]$表示从位置$i$到终点能获得的金币期望,则$dp[n]=n$,要求$dp[i]$只需要对$dp[i+1]$~$min\{dp[n],dp[i + 6]\}$求平均数,再加上$i$即可。

这是因为在位置$i$必然获得$i$元,然后根据$dp[i]$的性质继承前面的数据即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 105;
double dp[maxn];
int main()
{
int t, kase = 1;
scanf("%d", &t);
while(t--)
{
int n; scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%lf", &dp[i]);
//求概率
for(int i = n - 1; i >= 1; --i){
double tmp = 0;
for(int j = i + 1; j < i + 1 + min(6, n - i); ++j) tmp += dp[j];
dp[i] += (tmp / double(min(6, n - i)));
}
printf("Case %d: %.10lf\n", kase++, dp[1]);
}
return 0;
}

C - Race to 1 Again

分析

设$dp[i]$表示从$i$到1需要的次数期望,打$dp$的表即可。不过多注意细节。

注意在找一个数$x$的因子时,使用遍历$i$可以只遍历到$\sqrt x$,但注意此时它除了因子$i$还有一个$\frac{x}{i}$.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100500;
double dp[maxn] = {0};
int main()
{
int t;
//缩进不对是因为之前打表代码块放错位置了。。。
int n;
//打表
dp[0] = 0;
dp[1] = 0;
for(int i = 2; i < maxn; ++i){
n = 0;
int j;
for(j = 2; j * j < i; ++j){
if(i % j == 0){
dp[i] += dp[j] + dp[i / j];
n += 2;//两个因子
}
}
//注意特判平方
if(j * j == i){
n++;
dp[i] += dp[j];
}
n += 2;
dp[i] += n;
dp[i] /= n - 1;
}
scanf("%d", &t);
int kase = 1;
while(t--)
{
scanf("%d", &n);
printf("Case %d: %.7lf\n", kase++, n == 1 ? 0 : dp[n]);
}
return 0;
}

D - Birthday Paradox

题意

现在已知在地球上,人群人数大于等于23时,其中有2个人生日相同的概率大于50%。

现在在另外一个公转周期为$x$天的星球上,问有2个人生日相同的概率大于50%时,人群至少要多大?

分析

高中概率题,比较水

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <bits/stdc++.h>
using namespace std;
int kase = 0;
void work(double x)
{
double now = 1;
double i = 0;
while(now > 0.5)
{
now *= (x - i) / x;
i++;
}
printf("Case %d: %.0lf\n", ++kase, i - 1);
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
double n;
scanf("%lf", &n);
work(n);
}
return 0;
}

G - Dice(III)

题意

给一个N面骰子,每面相同,问投多少次,才能让所有面都正面朝上一次?求期望。

分析

递推一下。设$dp[i]$表示$n$面骰子$i$面朝上过一次的期望,$dp[1]=1$.

则$dp[i]=dp[i-1]+\frac{n}{n-(i-1)}$.

即$i$面朝上期望=$i-1$面朝上期望+要多投一面朝上的期望.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
#define eps 1e-8
using namespace std;
const int maxn = 100005;
double dp[maxn];
int n;
int main()
{
int t;
int kase = 1;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
dp[1] = 1;
for(int i = 2; i <= n; ++i){
dp[i] = dp[i - 1] + double(n) / double(n - (i - 1));
}
printf("Case %d: %.8f\n", kase++, dp[n]);
}
return 0;
}

H - Island of Survival

分析

首先贪心,为了避免老虎占比变大,遇到鹿肯定不要杀。

然后就是讨论+dp了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
double dp[maxn][maxn];
int t, d;
int main()
{
int T;
scanf("%d", &T);
int kase = 1;
while(T--)
{
scanf("%d%d", &t, &d);
if(t % 2 != 0){
printf("Case %d: 0.00000000\n", kase++);
continue;
}
memset(dp, 0, sizeof(dp));
dp[t][d] = 1;
for(int i = t; i >= 0; i -= 2){
for(int j = d; j >= 0; --j){
int sum = i * j + i * (i - 1) / 2 + i;
if(!sum) continue;
if(j) dp[i][j - 1] += dp[i][j] * i * j / sum;
if(i >= 2) dp[i-2][j] += dp[i][j] * i * (i - 1) / 2 / sum;
}
}
double ans = 0;
for(int i = 0; i <= d; ++i) ans += dp[0][i];
printf("Case %d: %.10f\n", kase++, ans);
}
return 0;
}