408 字
2 分钟
BZOJ 1415 [Noi2005] 聪聪和可可 概率DP
Description
Input
数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数。 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号。 接下来E行,每行两个整数,第i+2行的两个整数Ai和Bi表示景点Ai和景点Bi之间有一条路。 所有的路都是无向的,即:如果能从A走到B,就可以从B走到A。 输入保证任何两个景点之间不会有多于一条路直接相连,且聪聪和可可之间必有路直接或间接的相连。
Output
输出1个实数,四舍五入保留三位小数,表示平均多少个时间单位后聪聪会把可可吃掉。
Sample Input
【输入样例1】
4 3
1 4
1 2
2 3
3 4
【输入样例2】
9 9
9 3
1 2
2 3
3 4
4 5
3 6
4 6
4 7
7 8
8 9
Sample Output
【输出样例1】
1.500
【输出样例2】
2.167
题解
先每个点跑一遍SPFA
求出p[i][j]表示当可可在 j 点时,聪聪在 i 点要走的下一步
然后记忆化搜索
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct edge{
int END,next;
}v[100005];
int first[1005],Index,du[1005],n;
void add(int a,int b){
du[a]++;
v[Index].END=b;
v[Index].next=first[a];
first[a]=Index++;
}
int p[1005][1005];
double f[1005][1005];
void spfa(int x){
queue<int> Q;
bool flag[1005]={0};
int dis[1005],fr[1005];
memset(dis,0x3f,sizeof(dis));
memset(fr,0,sizeof(fr));
dis[x]=0;
flag[x]=1;Q.push(x);
while(!Q.empty()){
int k=Q.front();
Q.pop();flag[k]=0;
for(int i=first[k];i!=-1;i=v[i].next){
if(dis[v[i].END]>dis[k]+1||(dis[v[i].END]==dis[k]+1&&k<fr[v[i].END])){
dis[v[i].END]=dis[k]+1;
fr[v[i].END]=k;
if(!flag[v[i].END]){
flag[v[i].END]=1;
Q.push(v[i].END);
}
}
}
}
for(int i=1;i<=n;i++)
if(i!=x)
p[i][x]=fr[i];
}
double dfs(int s,int e){
if(s==e)return f[s][e]=0;
if(p[s][e]==e||p[p[s][e]][e]==e)return f[s][e]=1;
if(f[s][e]<=1e9) return f[s][e];
int tmp=p[p[s][e]][e];
f[s][e]=1;
for(int i=first[e];i!=-1;i=v[i].next){
f[s][e]+=dfs(tmp,v[i].END)/(du[e]+1);
}
f[s][e]+=dfs(tmp,e)/(du[e]+1);
return f[s][e];
}
int main()
{
//freopen("cchkk.in","r",stdin);
//freopen("cchkk.out","w",stdout);
memset(first,-1,sizeof(first));
memset(f,0x7f,sizeof(f));
int m,s,e,a,b;
scanf("%d%d%d%d",&n,&m,&s,&e);
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++)
spfa(i);
printf("%.3lf",dfs(s,e));
//while(1);
}
BZOJ 1415 [Noi2005] 聪聪和可可 概率DP
https://www.nekomio.com/posts/21/