HDU4418 Time travel 概率DP+高斯消元

题目大意:
给出N,M,E,S,D四个变量,以及每次走i步的概率P[i] (1<=i<=M) 且每次有D作为初始方向。 人从起点S出发判断走到E的步数期望,且每次走到端点会沿相反方向走:N=4 S=1 D=0 : 123210

1.判断 S==E 是否成立 -->成立则输出0.00, continue;

2.判断S出发是否最终能到达E(这里可采用bfs)–>不能则输出"Impoosible !"
为方便处理走到数轴终点折返的情况我们可设立数组:
如N=4时 使n=2*N-2;0123 变成 012345 (即 012321) 终点为e和n-e;
当D=0 可bfs(s) 当D=1 则bfs(n-s)

3.由题意可推出走到E的步数期望方程:dp[i]=∑(dp[i+k]+k)*p[i] (1<=k<=M)
<解释>dp[i+k]为点 (i+k)%n 走到 E 的步数期望 而 i 走到 E 则会经历 i 走到 (i+k)%n 再到 E 这一可能,且概率为 p[i]
dp[i]=∑(dp[i+k]+k)*p[k] --> dp[i] - ∑dp[i+k]p[k] = ∑kp[k]
–>E[i] - E[(i+1)%m]*p[1] - … - E[(i+m)%m]p[m] = ∑kp[k]
由步数期望方程形式可联想到解矩阵–高斯消元法

4.高斯消元 – 板子


const int maxn = 100;
int equ, var; //equ-->方程数 var-->未知量数
bool Gauss(double A[][maxn]) { //构建增广矩阵后以A[i][vol+1]存放常数向量
	for (int row = 1, col = 1; row <= equ && col <= var; ++row, ++col) {
		int max_r = row;
		for (int i = row + 1; i <= equ; ++i) 
			if (fabs(A[i][col]) > fabs(A[max_r][col])) max_r = i;
		if (fabs(A[max_r][col]) < eps) return 0; //防止多解
		if (max_r != row) {
			for (int i = col; i <= var; ++i) swap(A[max_r][i], A[row][i]);
			swap(A[max_r][var + 1], A[row][var + 1]);  //swap(E[max_r],E[row]) 
		}
		for (int i = row + 1; i <= equ; ++i) {
			double tmp = A[i][col] / A[row][col];
			for (int j = col; j <= var; ++j) A[i][j] -= A[row][j] * tmp;
			A[i][var + 1] -= A[row][var + 1] * tmp; //E[i]-=E[row]*tmp;
		}
	}
	for (int i = equ; i >= 1; --i) {
		for (int j = i + 1; j <= var; ++j) A[i][var + 1] -= A[i][j] * A[j][var + 1];
		A[i][var + 1] /= A[i][i];
	}
	/*for (int i = 1; i <= equ; ++i) {
		for (int j = 1; j <= var + 1; ++j) cout << A[i][j] << " ";
		cout << "\n";
	}*/
	return 1;
}

总代码如下

#pragma warning (disable :4996)
#include <bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const double PI = cos(-1.0);
const double eps = 1e-12;
#define For(i,n,m) for(int i=n;i<=m;++i)

int n, m, e, s, D, t;
int tot, equ, var;
int num[220];
double p[220], a[220][220], E[220];
const int maxn = 220;
//int equ, var; //equ-->方程数 var-->未知量数
bool Gauss(double A[][maxn]) { //构建增广矩阵后以A[i][vol+1]存放常数向量
	for (int row = 1, col = 1; row <= equ && col <= var; ++row, ++col) {
		int max_r = row;
		for (int i = row + 1; i <= equ; ++i) 
			if (fabs(A[i][col]) > fabs(A[max_r][col])) max_r = i;
		if (fabs(A[max_r][col]) < eps) return 0; //防止多解
		if (max_r != row) {
			for (int i = col; i <= var; ++i) swap(A[max_r][i], A[row][i]);
			swap(E[max_r], E[row]);
		}
		for (int i = row + 1; i <= equ; ++i) {
			double tmp = A[i][col] / A[row][col];
			for (int j = col; j <= var; ++j) A[i][j] -= A[row][j] * tmp;
			E[i] -= E[row] * tmp;
		}
	}
	for (int i = equ; i >= 1; --i) {
		for (int j = i + 1; j <= var; ++j) E[i] -= a[i][j] * E[j];
		E[i] /= A[i][i];
	}

	return 1;
}
void solve() {
	memset(a, 0, sizeof(a));
	memset(E, 0, sizeof(E));
	for (int i = 0; i < n; ++i) {
		if (num[i] == -1) continue;
		if (i == e || i == n - e) { E[num[i]] = 0, a[num[i]][num[i]] = 1; continue; }
		a[num[i]][num[i]] = 1;
		for (int j = 1; j <= m; ++j) {
			int tt = (i + j) % n;	
			a[num[i]][num[tt]] -= p[j];
			E[num[i]] += p[j] * j;
		}
	}
	if (Gauss(a)) cout << fixed << setprecision(2) << E[num[s]] << "\n";
	else cout << "Impossible !\n";
}
void bfs(int st) {
	memset(num, -1, sizeof(num));
	queue<int> q;
	q.push(st);
	tot = 0;
	num[st] = ++tot;
	while (!q.empty()) {
		int tmp = q.front();
		q.pop();
		For(i, 1, m) {
			if (fabs(p[i]) < eps) continue;
			int tt = (i + tmp) % n;
			if (num[tt] == -1) num[tt] = ++tot, q.push(tt);
		}
	}
}
int main() {
	ios::sync_with_stdio(false), cin.tie(NULL), cout.tie(NULL);
	cin >> t;
	while (t--) {
		cin >> n >> m >> e >> s >> D;
		For(i, 1, m) cin >> p[i], p[i] /= 100;
		if (s == e) { cout << "0.00\n"; continue; }
		n = n + n - 2;	
		if (D == 1) s = n - s;
		bfs(s);
		equ = var = tot;
		if (num[e] == -1 && num[n - e] == -1) { cout << "Impossible !\n"; continue; }
		solve();

	}
	return 0;

}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值