题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5001
解题思路:
题目大意:
给你一张无向的连通图,一共有n个点,m条边,随机从任意一点开始走,在任意一个点走向相连的点的概率都是相同的,一共走d步,分别求出每个点不在这个路径中的概率。
算法思想:
1.枚举每个要经过的点,然后进行状态转移,状态为dp[i][j],状态表示当前在j的点,已经走了i步,每次转移的时候,不从这个枚举的点出发,这样就可以求出所有路径经过该点的概率p, 然后1 - p就是不经过的答案
2.根据题目条件求出整张图的转移矩阵。然后对于每一个求值的点,将矩阵中以该点为起点的值的置0,然后利用矩阵快速幂,求出走了d步之后在其他的点的概率之和,即该点不在路径中的概率。
AC代码(概率dp+搜索):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N = 55;
const int M = 10005;
int n,m,d;
double dp[N][M];
vector<int> v[N];
double solve(int u){
double ans = 0;
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i = 0; i <= d; i++){
for(int j = 0; j <= n; j++){
if(u == j)
continue;
double p = 1.0 / v[j].size();
for (int k = 0; k < v[j].size(); k++) {
dp[v[j][k]][i + 1] += dp[j][i] * p;
}
}
ans += dp[u][i + 1];
}
return 1.0 - ans;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&d);
for(int i = 0; i <= n; i++)
v[i].clear();
int x,y;
for(int i = 0; i < m; i++){
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
for(int i = 1; i <= n; i++)
v[0].push_back(i);
for(int i = 1; i <= n; i++)
printf("%.10lf\n",solve(i));
}
return 0;
}
AC代码(矩阵快速幂):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 55;
vector<int> v[N];
int n,m,d;
struct matrix{
double num[N][N];
}val;
matrix mul(matrix a,matrix b){
matrix ret;
for(int i = 1; i <= n;i++){
for(int j = 1; j <= n;j++){
ret.num[i][j] = 0;
for(int k = 1;k <= n;k++)
ret.num[i][j] += a.num[i][k]*b.num[k][j];
}
}
return ret;
}
void solve(int n,int d,int x){//求不经过x点的概率
matrix ans,base = val;
memset(ans.num,0,sizeof(ans.num));
for(int i = 1;i <= n;i++)
ans.num[i][i] = 1;
for(int i = 1;i <= n;i++)
base.num[i][x] = 0;//对于所求点,将以该点为起点的值置0
//矩阵a次幂的结果num[i][j]表示以j为起点,走a步走到i点概率
while(d){
if(d&1)
ans = mul(ans,base);
base = mul(base,base);
d/=2;
}
double ret = 0;
for(int i = 1; i <= n; i++){
if(i == x)
continue;
for(int j = 1; j <= n; j++){
if(j == x)
continue;
ret += ans.num[i][j];//以j点为起点,不经过x点的情况下,走了d步最终走到i点的概率,求和即为所求点
}
}
printf("%.10f\n",ret/n);//矩阵快速幂求的结果默认每个起点的概率为1,实际为1/n,因此最终结果要除以n
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
int x,y;
scanf("%d%d%d",&n,&m,&d);
for(int i = 0; i <= n; i++)
v[i].clear();
for(int i = 0;i < m;i++){
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
memset(val.num,0,sizeof(val.num));
for(int i = 1; i <= n; i++){
int l = v[i].size();
for(int j = 0; j < l; j++){
int tmp = v[i][j];
val.num[i][tmp] = 1.0/v[tmp].size();//从tmp点走到i点的概率
val.num[tmp][i] = 1.0/v[i].size();//从i点走到tmp点的概率
}
}
for(int i=1;i<=n;i++)
solve(n,d,i);
}
return 0;
}