1 条题解
-
0
一、题意理解
我们有一个 个节点 条边的无向图:
- 每个节点 有:
- 游玩时间 ()
- 小 y 开心度增加
- 妹子开心度增加
- 每条边 有行走时间 ()
- 总时间预算 分钟()
- 规则:
- 初始随机选择一个节点开始
- 玩完当前节点后,从相邻且时间来得及的节点中等概率随机选下一个
- 如果没有可选的相邻节点,则结束
要求:计算小 y 和妹子开心度的期望。
二、关键点分析
这是一个带时间约束的随机游走问题。
由于 ,,且 都是整数,我们可以用动态规划。
三、状态设计
设 表示在节点 且剩余时间为 时,从该状态出发到结束,小 y 的期望开心度增量。
类似地, 表示妹子的期望开心度增量。
1. 状态转移
在节点 剩余时间 :
- 如果 ,则不能玩这个项目,。
- 否则,先玩项目 ,耗时 ,获得开心度 ,然后剩余时间 。
- 接着,查看 的所有邻居 :
- 如果从 到 的边权 满足 ,则 是可达的。
- 设可达邻居集合为 ,大小 。
- 如果 ,则结束,。
- 否则,等概率选择邻居: $ dp[u][t] = h1_u + \frac{1}{cnt} \sum_{v \in adj_u} dp[v][t' - w_{uv}] $ 其中 是边权。
同理计算 。
四、初始状态
初始时随机选择一个节点 ,剩余时间 。
所以最终期望:
五、算法步骤
- 读入 和节点数据、边数据。
- 建图(邻接表)。
- 初始化 (或未计算标记)。
- 记忆化搜索计算 和 :
- 如果 ,返回 0
- 计算可达邻居
- 按上述公式递归计算
- 对每个起点 计算 和 ,求平均。
- 输出保留 5 位小数。
六、复杂度
- 状态数:
- 每个状态最多检查 个邻居 → 总 ,可接受。
七、样例验证
样例:
n=5, m=4, k=60 节点数据: 1: 25 12 83 2: 30 38 90 3: 16 13 70 4: 22 15 63 5: 50 72 18 边: 2-1:7 3-1:7 4-3:1 5-3:10输出:
39.20000 114.40000用上述 DP 计算可得该结果。
八、代码实现(C++)
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 110, M = 500; int c[N], h[N][3], du[N][M]; double p[N][M][3], f[N][M][3]; struct node { int v, w; }; vector<node>e[N]; void add(int u, int v, int w) { e[u].push_back((node) { v, w }); } signed main() { int n, m, K; cin >> n >> m >> K; for (int i = 1; i <= n; i++) { scanf("%lld%lld%lld", &c[i], &h[i][1], &h[i][2]); } for (int i = 1; i <= m; i++) { int u, v, w; scanf("%lld%lld%lld", &u, &v, &w); add(u, v, w); add(v, u, w); } for (int i = 1; i <= n; i++) { for (int j = 0; j <= K; j++) { for (node u : e[i]) { int v = u.v, w = u.w, ned = c[v] + w; if (j + ned <= K) { du[i][j]++; } } } } for (int i = 1; i <= 2; i++) { for (int j = 1; j <= n; j++) { f[j][c[j]][i] = 1.0 * h[j][i] / n; p[j][c[j]][i] = 1.0 / n; } for (int k = 1; k <= K; k++) { for (int j = 1; j <= n; j++) { for (node u : e[j]) { int v = u.v, w = u.w, ned = c[j] + w, nn = k - ned; if (du[v][nn] == 0 || p[v][nn][i] == 0) { continue; } if (k >= ned) { double t = p[v][nn][i] / du[v][nn]; f[j][k][i] += f[v][nn][i] / du[v][nn]; p[j][k][i] += t; f[j][k][i] += h[j][i] * t; } } } } } double ans[3] = {}; for (int i = 1; i <= 2; i++) { for (int j = 1; j <= n; j++) { for (int k = 0; k <= K; k++) { if (du[j][k] == 0) { ans[i] += f[j][k][i]; } } } } printf("%0.5f %0.5f\n", ans[1], ans[2]); return 0; }
九、总结
本题的关键在于:
- 将问题建模为带时间约束的随机游走。
- 使用记忆化搜索计算期望开心度。
- 状态定义为
(当前节点, 剩余时间)。 - 转移时检查可达邻居并等概率随机选择。
- 最终对所有起点求平均。
- 每个节点 有:
- 1
信息
- ID
- 4920
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者