期望DP

整理一些期望dp的题目,会不断更新

BZOJ4318 OSU!

题意

(;)
现在你有一个长度为(n)的字符串,对于第i位来说,有(a(i))的概率是1,现在定义你获得分数为每一段极长连续的1的长度的立方和。
例如:00100010的分数即为(2^3+3^3+1^3=36)
数据范围:(nleq 10^5)
(;)

Solution

(;)
我们用(f_0(i))表示以i为结尾的连续的一段1的长度的期望
那么第i位有(a(i))的概率是1,则对于这种情况的长度,即为(f_0(i-1)+1)
否则说明是0,那么长度一定为0
所以(f_0(i)=a(i) imes (f_0(i-1)+1))
(;)
然后我们用(f_1(i))表示以i为结尾的连续的一段1的长度平方的期望
根据刚才的转移方程,我们可以得到:
(E[f_1(i)]=E[f_0(i)^2]=E[a(i) imes (f_0(i-1)+1)^2]=E[a(i) imes (f_1(i-1)+2f_0(i-1)+1)]=a(i) imes (E[f_1(i-1)]+2E[f_0(i-1)]+1))
同理(f_2(i))表示以i为结尾的连续的一段1的长度的立方的期望,即:
(E[f_2(i)]=E[f_0(i)^3]=E[a(i) imes (f_0(i-1)+1)^3]=a(i) imes E[f_2(i-1)+3f_1(i-1)+3f_0(i-1)+1]=a(i) imes (E[f_2(i-1)]+3E[f_1(i-1)]+3E[f_0(i-1)]+1))
那么统计答案的时候考虑以每一位为结尾的一段1,则需要满足(i+1)位为0,即:((1-a_{i+1}) imes f_2(i))
时间复杂度:(O(n))
(;)

BZOJ3143 游走

题意

(;)
现在你有一个(n)个点(m)条边的无向连通图,从1号点出发,每一步会等概率地选择当前点连出去地某条边进行游走,到(n)号点停止。
现在你要为这(m)条边分配编号,定义一条边的长度为这条边的编号,求从1号点到(n)号点经过长度的最小期望
数据范围:(nleq 500 ;;m leq 125000)
(;)

Solution

(;)

Step 1

(;)
如何分配边的id?
首先我们有一个贪心的思路:因为我们从1号点到(n)号点的这条路径上的每条边可能会经过多次
所以我们要使经过次数的多的边的id尽可能地小,使经过次数少的边的id尽可能的大
所以问题就被转化为了如何求经过一条边的次数的期望
(;)

Step 2

(;)
而对于((u,v))这条边,由于是无向的,所以有可能是(u->v)(v->u)
所以(E[edge(u,v)]=frac{1}{deg[u]} imes E[u] +frac{1}{deg[v]} imes E[v])
所以问题进而转化为如何求经过一个点的次数的期望
(;)

Step 3

(;)
那么问题到这里就变得简单了
本质与绿豆蛙的归宿这道题一样,dp转移方程即为:
(E[v]=sum_{in edge(u,v)}frac{1}{deg[u]} imes E[u])
而这个东西是又后效性的,我们无法直接递推的求
所以我们把刚刚那个柿子移项,写成如下形式:
(a_1E[1]+a_2E[2]+cdots+a_nE[n]=a_{n+1})
那么对于这(n)个点来说,就能写出(n)个这样的方程。
所以,直接高斯消元求解。
(;)

Step 4

(;)
如何统计答案?
首先我们将边按(E[])从小到大排序,这样我们也能把每一条边的(id)给确定下来
然后我们对于每条边分别考虑贡献,那么(edge(u,v))这条边的贡献显然为(id imes E[edge(u,v)])
时间复杂度:(O(n^3+mlog(m)))
(;)

Code

#include <bits/stdc++.h>
const int N = 510, M = 200010;
int n, m, d[N][N], deg[N];
double dp[N], g[N][N], res[N], f[M];
struct Edge
{
	int from, to;
}e[M];
void gauss()
{
	for(int i=1;i<n;i++)
	{
		for(int j=i+1;j<n;j++)
		{
			if(fabs(g[j][i]) > fabs(g[i][i]))
			{
				for(int k=1;k<=n+1;k++)
				{
					std::swap(g[i][k], g[j][k]);
				}
			}
		}
		if(fabs(g[i][i]) <= 1e-8) continue;
		for(int j=i+1;j<n;j++)
		{
			double tmp = g[j][i] / g[i][i];
			for(int k=1;k<=n+1;k++) 
			{
				g[j][k] = g[j][k] - tmp * g[i][k];
			}
		}
	}
}
int main()
{
	scanf("%d%d", &n, &m);
	for(int i=1;i<=m;i++)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		d[u][v] = d[v][u] = 1; deg[u] ++; deg[v] ++;
		e[i].from = u; e[i].to = v;
	}
	for(int i=1;i<n;i++)
	{
		g[i][i] = -1.0;
		for(int j=1;j<n;j++)
		{
			if(d[i][j]) 
			{
				g[i][j] = (double) 1 / deg[j];
			}
		} 
		if(i == 1) g[i][n + 1] = -1.0;
	}
	gauss();
	for(int i=n-1;i>=1;i--)
	{
		double now = 0.0;
		for(int j=i+1;j<n;j++) now = now + g[i][j] * res[j];
		res[i] = (g[i][n + 1] - now) / g[i][i];
	}
	for(int i=1;i<=m;i++)
	{
		int u = e[i].from, v = e[i].to;
		if(u != n) f[i] += res[u] / deg[u];
		if(v != n) f[i] += res[v] / deg[v];
	}
	std::sort(f + 1, f + m + 1);
	double ans = 0;
	for(int i=1;i<=m;i++)
	{
		ans += f[i] * (m - i + 1);
	}
	printf("%.3lf", ans);
	return 0;
} 
原文地址:https://www.cnblogs.com/czyty114/p/13762504.html