洛谷题单指南-基础线性代数-P2973 [USACO10HOL] Driving Out the Piggies G
原题链接:https://www.luogu.com.cn/problem/P2973
题意解读:
给定一张无向图:N 个点M 条边
炸弹从 1 号城市开始。每一小时:以概率P=p/q在当前城市爆炸,以概率1−P随机选择一条边走到相邻城市(每条边等概率)。
问题:最终炸弹在每个城市爆炸的概率是多少。
解题思路:(题解由Chatgpt生成)
路径长度无限:
可能出现
1
1→2
1→2→1
1→2→1→2
...
1→2
1→2→1
1→2→1→2
...
因此:无法简单 DP,需要求无限过程的稳态概率
关键技巧:把问题转化为期望访问次数




n个未知数,n个方程,高斯消元即可。

100分代码:(代码由Chatgpt生成)
#include <bits/stdc++.h>
using namespace std;
const int N = 305;
const double EPS = 1e-8;
// 增广矩阵 a,规模 n * (n+1),下标从 1 开始使用
// a[i][1..n] 为系数,a[i][n+1] 为常数项
double a[N][N];
// 无向图邻接表与度数
vector<int> g[N];
int deg[N];
int n, m;
double P; // P = p / q
// 高斯消元(带部分主元),返回:
// 1 -> 唯一解;0 -> 无穷多解;-1 -> 无解
int gauss()
{
int row = 1;
// 枚举列,逐步把主元行放到 row 位置
for(int col = 1; col <= n; col++)
{
int t = row;
// 在当前列从 row..n 选择绝对值最大的作为主元
for(int i = row + 1; i <= n; i++)
if(fabs(a[i][col]) > fabs(a[t][col]))
t = i;
// 该列全为 0,无法形成主元,跳过
if(fabs(a[t][col]) < EPS) continue;
// 把主元行交换到 row
for(int i = col; i <= n + 1; i++)
swap(a[t][i], a[row][i]);
// 主元行归一化,使 a[row][col] = 1
for(int i = n + 1; i >= col; i--)
a[row][i] /= a[row][col];
// 用当前主元消去其他行的该列
for(int i = 1; i <= n; i++)
{
if(i == row) continue;
for(int j = n + 1; j >= col; j--)
a[i][j] -= a[row][j] * a[i][col];
}
row++;
}
// row <= n 说明存在自由元,需判断是否矛盾
if(row <= n)
{
for(int i = row; i <= n; i++)
if(fabs(a[i][n + 1]) > EPS)
return -1;
return 0;
}
return 1;
}
int main()
{
int p, q;
cin >> n >> m >> p >> q;
P = (double)p / q;
// 读入无向边,建图
for(int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
// 统计每个点度数
for(int i = 1; i <= n; i++)
deg[i] = g[i].size();
// 构造线性方程:
// 设 x[i] 为到达目标(或被选中)的概率/期望值(与题意对应),
// 转移:从 i 出发,以 P 结束,否则以 1-P 随机走到邻居。
// 具体形式为:
// x[i] - sum_{j in g[i]} (1-P)/deg[j] * x[j] = (i==1 ? 1 : 0)
// 右端常数项根据题意设置(起点 1 的常数为 1,其余为 0)。
// 注:系数中出现 deg[j],是根据题目给定的转移方式整理而来。
for(int i = 1; i <= n; i++)
{
// x[i] 系数为 1
a[i][i] = 1;
// 邻居节点对当前方程的贡献
for(int j : g[i])
a[i][j] -= (1 - P) / deg[j];
// 常数项
if(i == 1) a[i][n + 1] = 1;
else a[i][n + 1] = 0;
}
// 求解线性方程组
gauss();
// 输出答案:题意要求乘以 P(结束事件的概率)
for(int i = 1; i <= n; i++)
printf("%.9f\n", a[i][n + 1] * P);
return 0;
}
浙公网安备 33010602011771号