绿豆蛙的归宿

link

emm,题目本身并不见得很难,主要是有一个点必须理清楚。期望DP的计算需要遵守全期望公式,大概是\(E=\sum\limits_i^m p_i\times c_i\),也就是说你在搞清楚一个东西对于期望的贡献的同时还应该要知道它产生贡献的概率。这也就回答了为什么本题中只能逆推而不能顺推的问题。毕竟一个点要走到终点必须也只能经过它的某条出边,所以经过某条出边的概率是一定的;而顺推时,你要从某条入边到当前点的概率为来点被经过的概率再除以来点的出度,显然要麻烦一些。但第二种也并不是不能写,可以考虑对期望贡献进行拆分,计算每条边被贡献的概率,递推时只用计算经过某个点的概率即可。于是就产生了两种写法:

第一种逆推:

#include<cstdio>
#include<queue>
#include<iostream>
//#define zczc
using namespace std;
const int N=200010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
int m,n,s1,s2,s3,d[N],s[N],a[N],cnt;
struct edge{int t,v,next;}e[N];
int head[N],esum;
inline void add(int fr,int to,int val){
	esum++;e[esum].t=to;
	e[esum].v=val;e[esum].next=head[fr];head[fr]=esum;
}
queue<int>q;
double f[N]={0};
signed main(){
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	read(m);read(n);
	for(int i=1;i<=n;i++){
		read(s1);read(s2);read(s3);
		add(s1,s2,s3);d[s2]++,s[s1]++;
	}
	for(int i=1;i<=m;i++)
		if(d[i]==0)q.push(i);
	while(!q.empty()){
		int now=q.front();q.pop();a[++cnt]=now;
		for(int i=head[now];i;i=e[i].next){
			if(--d[e[i].t]==0)q.push(e[i].t);
		}
	}
	f[m]=0;
	for(int i=m-1;i;i--){
		int wh=a[i];
		for(int j=head[wh];j;j=e[j].next){
			f[wh]+=(double)(f[e[j].t]+e[j].v)/s[wh];
		}
	}
	printf("%.2f",f[1]);
	return 0;
}

第二种贡献拆分:

#include<cstdio>
#include<queue>
#include<iostream>
//#define zczc
using namespace std;
const int N=200010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

int m,n,s1,s2,s3,d[N],s[N],a[N],cnt;
struct edge{
	int t,v,next;
}e[N];
int head[N],esum;
inline void add(int fr,int to,int val){
	esum++;
	e[esum].t=to;
	e[esum].v=val; 
	e[esum].next=head[fr];
	head[fr]=esum;
	return;
}

queue<int>q;
double f[N]={0},ans=0;

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=1;i<=n;i++){
		read(s1);read(s2);read(s3);
		add(s1,s2,s3);d[s2]++;s[s1]++;
	}
	for(int i=1;i<=m;i++)if(d[i]==0)q.push(i);
	f[1]=1;
	while(!q.empty()){
		int now=q.front();q.pop();
		if(now==m)break;
		for(int i=head[now];i;i=e[i].next){
			f[e[i].t]+=f[now]/s[now];
			ans+=e[i].v*f[now]/s[now];
			if(--d[e[i].t]==0)q.push(e[i].t);
		}
	}
	printf("%.2f",ans);
	
	return 0;
}

这道黄题对于图上期望DP具有借鉴意义(吧)。

上一篇:王道机试笔记


下一篇:Ancient Cipher