Walk
The nation looks like a connected bidirectional graph, and I am randomly walking on it. It means when I am at node i, I will travel to an adjacent node with the same probability in the next step. I will pick up the start node randomly (each node in the graph has the same probability.), and travel for d steps, noting that I may go through some nodes multiple times.
If I miss some sights at a node, it will make me unhappy. So I wonder for each node, what is the probability that my path doesn't contain it.
For each test case, the first line contains 3 integers n, m and d, denoting the number of vertices, the number of edges and the number of steps respectively. Then m lines follows, each containing two integers a and b, denoting there is an edge between node a and node b.
T<=20, n<=50, n-1<=m<=n*(n-1)/2, 1<=d<=10000. There is no self-loops or multiple edges in the graph, and the graph is connected. The nodes are indexed from 1.
Your answer will be accepted if its absolute error doesn't exceed 1e-5.
2 5 10 100 1 2 2 3 3 4 4 5 1 5 2 4 3 5 2 5 1 4 1 3 10 10 10 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 4 9
0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.6993317967 0.5864284952 0.4440860821 0.2275896991 0.4294074591 0.4851048742 0.4896018842 0.4525044250 0.3406567483 0.6421630037
题意为图中有n个顶点编号1到n,m条双向边,以及走d步,一开始随机在n个点中选择一个作为起点,然后在图中走d步,一个顶点向其每个临接点走的可能性相等,一个顶点可以多次到达,要求在所有的路径中不经过点i的概率(1<=i<=n)。
比赛时用bfs,dfs写了好长时间,还是没弄出来,就是不清楚状态怎么保存,到底是加还是乘,每条路径怎么记录。。。愣是没想到用dp[][]数组来做,哎。。。
状态用dp[i][j]保存,定义dp[i][j]为第j步到达第i个点的概率。
要求不经过第i个点的概率,也就是求到达其它点的概率,QQ群里面大神们讨论的“删点” (现在才明白T T)。怎么样才能保证不经过第i个点呢,首先起点不能从第i个点出发,其次,当一个点向其临接点转移时,不能到达第i个点。最后把dp[ j ] [ d ] , (j!=i)加起来就是所求。仔细一想,其实第二条没有什么影响,当前点j向其临接点转移的时候,到达每个点的概率是 1.0/g[j].size() (vector<int>g[52],临接表),就算到达第i个点,也不影响到达其它临接点的概率,而且最后相加的时候也和dp[i][]没关系。
代码:
#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
#include <iomanip>
using namespace std;
double dp[51][10010];//dp[i][j]代表第j步到达第i个点的概率
vector<int>g[51];//邻接表
double ans;
int n,m,d;
void clr()
{
for(int i=1;i<=n;i++)
g[i].clear();
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
clr();
scanf("%d%d%d",&n,&m,&d);
int from,to;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&from,&to);
g[from].push_back(to);
g[to].push_back(from);
}
for(int i=1;i<=n;i++)//枚举每个点
{
ans=0;
memset(dp,0,sizeof(dp));
for(int j=1;j<=n;j++)
dp[j][0]=1.0/n;
for(int j=1;j<=d;j++)//枚举步数
{
for(from=1;from<=n;from++)//从哪里开始走
{
if(from==i)
continue;
for(int k=0;k<g[from].size();k++)//到哪里去
if(g[from][k]!=i)//其实不加这一句也没有影响,因为就算下一步到达的点中有i,到达其他的点的概率也是1.0/g[from].size()
dp[g[from][k]][j]+=dp[from][j-1]*(1.0/g[from].size());
}
}
for(int j=1;j<=n;j++)
if(j!=i)
ans+=dp[j][d];//ans为经过除第i个点以外其他点的概率,也就是不经过第i个点的概率
cout<<setiosflags(ios::fixed)<<setprecision(10)<<ans<<endl;
}
}
return 0;
}
一开始写的一份代码是求经过第i点的概率,最后1减去它即为所求,可是样例都通不过。。求指导。
代码:
#include <iostream>
#include <vector>
#include <string.h>
#include <iomanip>
#include <stdio.h>
using namespace std;
const int maxn=51;
double dp[maxn][10002];
vector<int>g[maxn];
int t,n,m,d;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&d);
for(int i=0;i<=n;i++)
g[i].clear();
int from,to;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&from,&to);
g[from].push_back(to);
g[to].push_back(from);
}
for(int i=1;i<=n;i++)
g[0].push_back(i);
for(int i=1;i<=n;i++)//枚举每个点
{
memset(dp,0,sizeof(dp));
double ans=0.0;
for(int j=1;j<=n;j++)
dp[j][0]=1.0/n;
for(int j=1;j<=d;j++)//走多少步
{
for(from=0;from<=n;from++)//从哪里开始走
{
if(from==i)//不从枚举的当前点开始走
continue;
for(to=0;to<g[from].size();to++)//到哪里去
{
dp[g[from][to]][j]+=dp[from][j-1]*1.0/g[from].size();
}
}
ans+=dp[i][j];
}
cout<<setiosflags(ios::fixed)<<setprecision(10)<<1-ans<<endl;
}
}
return 0;
}