【总结】待定系数求解高斯消元
摘要:运用待定系数法建立方程求解期望值。
「HDU4035」Maze
本题 n n n 比较大,很难用高斯消元求解。
考虑叶子节点,有关系式:
d p [ i ] = k [ i ] ∗ d p [ 1 ] + ( 1 − k [ i ] − e [ i ] ) ( d p [ f a [ i ] ] + 1 ) dp[i]=k[i]*dp[1] + (1-k[i]-e[i]) (dp[fa[i]]+1) dp[i]=k[i]∗dp[1]+(1−k[i]−e[i])(dp[fa[i]]+1)
这启发我们用待定系数法来求解。
一般地,设 d p [ i ] = A [ i ] ∗ d p [ 1 ] + B [ i ] ∗ d p [ f a [ i ] ] + C [ i ] dp[i]=A[i]*dp[1] + B[i]*dp[fa[i]] + C[i] dp[i]=A[i]∗dp[1]+B[i]∗dp[fa[i]]+C[i]
对于一般的节点,有转移式:
d p [ i ] = k [ i ] ∗ d p [ 1 ] + ( 1 − e [ i ] − k [ i ] ) ∗ ∑ j ∈ s o n ( i ) ( d p [ j ] + 1 ) m dp[i]=k[i]*dp[1]+\frac{(1-e[i]-k[i])*\sum_{j\in son(i)}(dp[j]+1)}{m} dp[i]=k[i]∗dp[1]+m(1−e[i]−k[i])∗∑j∈son(i)(dp[j]+1)
最后再解一个方程即可。
#include<bits/stdc++.h>
#define db double
using namespace std;
const int Maxn=10005;
const db eps=1e-9;
int T,n,Case,in[Maxn];
db e[Maxn],k[Maxn];
db A[Maxn],B[Maxn],C[Maxn],D[Maxn];
vector<int> g[Maxn];
bool dfs(int x,int fa) {
	A[x]=B[x]=C[x]=D[x]=0;
	int m=g[x].size();
	A[x]=k[x];
	B[x]=(1-k[x]-e[x])/m;
	C[x]=1-k[x]-e[x];
	for(auto y:g[x]) {
		if(y==fa) continue;
		if(!dfs(y,x)) return 0;
		A[x]+=(1-k[x]-e[x])/m*A[y];
		D[x]+=(1-k[x]-e[x])/m*B[y];
		C[x]+=(1-k[x]-e[x])/m*C[y];
	}
	if(fabs(D[x]-1)<=eps) {
		return 0;
	}
	A[x]/=1-D[x];
	B[x]/=1-D[x];
	C[x]/=1-D[x];
	return 1;
}
int main() {
//	freopen("1.in","r",stdin);
	scanf("%d",&T);
	while(T--) {
		int flg=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) in[i]=0,g[i].clear();
		for(int i=1;i<n;i++) {
			int u,v;
			scanf("%d%d",&u,&v);
			g[u].push_back(v); 
			g[v].push_back(u);
			in[u]++,in[v]++;
		}
	    for(int i=1;i<=n;i++) {
	    	scanf("%lf%lf",&k[i],&e[i]);
	    	e[i]/=100;
	    	k[i]/=100;
		}
		if(!dfs(1,0)||fabs(A[1]-1)<=eps) {
			printf("Case %d: impossible\n",++Case);
		}
		else {
			printf("Case %d: %.6lf\n",++Case,C[1]/(1-A[1]));
		}
	}
}
「ZOJ3329」One Person Game
考虑逆推。
设 d p [ i ] dp[i] dp[i] 表示当前在第 i i i 格时到达终点的期望步数。
设骰子为 (a,b,c) 的概率为 p0 ,否则总和为 j 的概率为 pj 。
不难得到这样的式子:
d p [ i ] = ∑ j = 0 a + b + c p [ j ] ∗ d p [ i + j ] + p [ 0 ] ∗ d p [ 1 ] dp[i]=\sum_{j=0}^{a+b+c}p[j]*dp[i+j]+p[0]*dp[1] dp[i]=∑j=0a+b+cp[j]∗dp[i+j]+p[0]∗dp[1]
直接消元时间复杂度 O ( n 3 ) O(n^3) O(n3) 。
观察到每一项都和 d p [ 0 ] dp[0] dp[0] 有关。
设 d p [ i ] = A [ i ] ∗ d p [ 0 ] + B [ i ] dp[i]=A[i]*dp[0]+B[i] dp[i]=A[i]∗dp[0]+B[i]
A[i] , B[i] 不难递推得到。
最后解出 d p [ 0 ] dp[0] dp[0] 的方程即可。
总结:本题设而不求时关键。以及递推思想的运用。
#include<bits/stdc++.h>
#define db double
using namespace std;
const int Maxn=505;
int T,n,k1,k2,k3,a,b,c;
db A[Maxn],B[Maxn],p[Maxn];
int main() {
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
		A[n+1]=B[n+1]=0;
		p[0]=1.0/k1/k2/k3;
		for(int i=1;i<=k1+k2+k3;i++) p[i]=0;
		for(int i=1;i<=k1;i++) {
			for(int j=1;j<=k2;j++) {
				for(int k=1;k<=k3;k++) {
					if(i==a&&j==b&&k==c) continue;
					p[i+j+k]+=1.0/k1/k2/k3;
				}
			}
		}
		for(int i=n;i>=0;i--) {
			A[i]=p[0];
			B[i]=1;
			for(int j=1;j<=k1+k2+k3;j++) {
				if(i+j>n) continue;
				A[i]+=p[j]*A[i+j];
				B[i]+=p[j]*B[i+j];
			}
		}
		printf("%.12lf\n",B[0]/(1-A[0]));
	}
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号