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\) 号点的位置的概率。

我们目前不需要考虑第三维的问题,因为两个人概率的递推式是一样的,所以下面的递推式都把最后一维省略掉了,最后写程序的时候加上就好了。

根据状态和题目要求,我们列出递推式:

\[f_{i,j} = \sum\limits_{mapp[j][k] = true} dp[i - 1][k] \div (deg[k] + 1) \]

其中 \(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;
}
posted @ 2022-07-22 21:27  Night_Tide  阅读(42)  评论(0)    收藏  举报