题目链接:https://www.oj.swust.edu.cn/problem/show/2620
题目大意:给一个n个点m条边的图,保证数据无重边,无自环。顶点编号1到n,一个人从1出发,每一分钟,会等概率地走到和她当前所在点相邻的点。问你经过k分钟后,这个人在各个点的概率
数据范围:2<=n<=200 、1<=m<=(n-1)*n/2、k<=10^5
题目分析:可以把这个人从i点到另外n个点(包括自己)的概率列出来,可以构造一个n*n的矩阵。因为这个人从1出发的,所以初始矩阵是定了的[1,0,0,0,…,0],代表在第一分钟里,他在1号节点的概率是1,其他地方是0,然后矩阵乘法即可。这里由于k很大,所以需要用二分幂矩阵。
这个题比较有意思,如果学习过机器学习的知识的话,应该能反应过来这个题是一个比较明显马尔科夫矩阵问题。
代码:
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<iostream>
using namespace std;
vector<int>grid[205];
double markov[205][205];
void init_markov_matrix(int n)
{
int i,j;
for(i=0;i<=n;++i)
{
grid[i].clear();
for(j=0;j<=n;++j)
markov[i][j]=0;
}
}
//矩阵乘法
void muilt_martix(double P[][205],double Q[][205],int n)
{
int i,j,k;
double X[205][205];
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
{
X[i][j]=0;
for(k=1;k<=n;++k)
X[i][j]+=P[i][k]*Q[k][j];
}
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
P[i][j]=X[i][j];
}
//矩阵乘方快速算法
void markov_power(int n,int k,double P[][205])
{
double Q[205][205];
int i,j;
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
{
Q[i][j]=markov[i][j];
if(i==j)
P[i][j]=1;
else
P[i][j]=0;
}
if(k==0)
return;
while(k!=1)
{
if(k&1)
{
--k;
muilt_martix(P,Q,n);
}
else
{
k=k>>1;
muilt_martix(Q,Q,n);
}
}
muilt_martix(P,Q,n);
}
int main()
{
int n,m,k,i,j,u,v;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
init_markov_matrix(n);
for(i=0;i<m;++i)
{
scanf("%d%d",&u,&v);
grid[u].push_back(v);
grid[v].push_back(u);
}
vector<int>::iterator iter;
double M[205],S[205];
for(i=1;i<=n;++i)
{
if(i==1)
M[i]=1;
else
M[i]=0;
double rate=1.0/double(grid[i].size());
for(iter=grid[i].begin();iter!=grid[i].end();++iter)
markov[i][*iter]=rate;
markov[i][i]=0.0;
}
double P[205][205];
//马尔科夫矩阵自乘K次,再乘以[1,0,0,0,0...,0]即答案(取第一行)
markov_power(n,k,P);
for(i=1;i<n;++i)
printf("%lf ",P[1][i]);
printf("%lf\n",P[1][n]);
}
return 0;
}