UVA10091 The Valentine's Day 题解
\(Update\ 2022/6/11:\) 修改了部分细节错误
PART 0:广告
无耻推荐个人博客
PART 1:题目大意
给定一个 \(26\) 个点, \(R\) 条边的无向图。从每个点出发到与之之间有边的点和原地不动的概率相等。求两个人分别从两个点出发,同时移动给定次数后两个人位于同一点的概率。
PART 2:解题思路
明显的概率 DP。
考虑设状态 \(f_{i,j}\) 表示移动 \(i\) 次后位于 \(j\) 号点的位置的概率。由于有两个人,我们在添加一维,\(f_{i,j,0/1}\) ,其中 \(0\) 表示 \(A\) 移动 \(i\) 次后位于 \(j\) 号点的位置的概率,其中 \(1\) 表示 \(B\) 移动 \(i\) 次后位于 \(j\) 号点的位置的概率。
我们目前不需要考虑第三维的问题,因为两个人概率的递推式是一样的,所以下面的递推式都把最后一维省略掉了,最后写程序的时候加上就好了。
根据状态和题目要求,我们列出递推式:
其中 \(mapp\) 数组用邻接矩阵存储无向图,\(mapp[i][j] = true\) 表示 \(i\) 和 \(j\) 之间有边。\(deg[i]\) 表示 \(i\) 的度数,即以 \(i\) 为其中一个端点的边的数量,加 \(1\) 是因为还有原地不动的可能。
边界是 \(f_{0,b,0} = 1, f_{0,b,1} = 1\),其中 \(a\) 和 \(b\) 分别是输入给出的 \(A\) 和 \(B\) 两人的初始位置。
关于给定次数计算的一些细节
至于所谓给定次数,实际上就是初始时间与下一个情人节之间经过了多少个月。
-
一般来说,给定次数 的值就算初始时间的月份与下一个二月之间经过了多少个月。
-
要注意的是,由于第一个情人节是在公元 \(470\) 年开始的,而数据范围中的年份最小可以到 \(460\) 年,这里需要注意
-
还有一点是如果初始时间已经过了初始年份的情人节,那么需要等到第二年的情人节。也就是说 \(year \ge 470\) 且 \(month = 2\) 且 \(day \ge 14\),那么给定次数要加上 \(12\)。而如果没有过初始年份的情人节,那给定次数就是 \(0\)
PART 3:完整代码
下面的程序中用 \(m\) 代表题目描述中的 \(R\),\(dp\) 数组就是递推式中的 \(f\)。
#include<bits/stdc++.h>
#define MAXN 50
#define MAXM 150
using namespace std;
int n,m,t,a,b,cnt,year,month,day;
int mapp[MAXN][MAXN],deg[MAXN];
double dp[MAXM][MAXN][2];
int main(){
n = 26;
scanf("%d",&t);
while(t--){
memset(mapp, 0, sizeof(mapp));
memset(deg, 0, sizeof(deg));
memset(dp, 0, sizeof(dp));
scanf("%d%d%d%d",&year,&month,&day,&m);
for(int i = 1; i <= m; i++){
char tmp1[5], tmp2[5], u, v;
scanf("%s%s",tmp1,tmp2);
u = tmp1[0], v = tmp2[0];
mapp[u - 'A' + 1][v - 'A' + 1] = mapp[v - 'A' + 1][u - 'A' + 1] = true;
}
for(int i = 1; i <= n; i++) mapp[i][i] = true;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(mapp[i][j]) deg[i]++;
}
}
char tmp1[5], tmp2[5];
scanf("%s%s",tmp1,tmp2);
a = tmp1[0] - 'A' + 1; b = tmp2[0] - 'A' + 1;
dp[0][a][0] = dp[0][b][1] = 1;
int cnt = 0;
if(year >= 470 && month == 2 && day >= 14) cnt += 12;
while(year < 470 || month != 2){
month++; cnt++;
if(month > 12){
month %= 12;
year++;
}
}
for(int i = 1; i <= cnt; i++){
for(int j = 1; j <= n; j++){
for(int k = 1; k <= n; k++){
if(mapp[k][j]){
dp[i][j][0] += dp[i - 1][k][0] / deg[k];
dp[i][j][1] += dp[i - 1][k][1] / deg[k];
}
}
}
}
double ans = 0;
for(int i = 1; i <= n; i++) ans += dp[cnt][i][0] * dp[cnt][i][1];
printf("%.5lf\n",ans);
}
return 0;
}

浙公网安备 33010602011771号