【HDU】4418 Time travel

http://acm.hdu.edu.cn/showproblem.php?pid=4418

题意:一个0~n-1的坐标轴,给出起点X、终点Y,和初始方向D(0表示从左向右、1表示从右向左,-1表示起点或终点),在走的过程中如果到达起点或终点,那么下一步往反方向走。每次可以走1~m步,每步概率为p[i],问走到终点的期望步数。(n,m,X,Y<=100)

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=205;
const double eps=1e-6;
double A[N][N], p[N], C;
inline double abs(double x) { return x<0?-x:x; }

int guass(int n, int m) {
	int x=1, y=1;
	while(x<=n && y<=m) {
		int pos=x;
		for(int i=x+1; i<=n; ++i) if(abs(A[i][y])>abs(A[pos][y])) pos=i;
		if(abs(A[pos][y])<eps) { ++y; continue; }
		for(int i=1; i<=m+1; ++i) swap(A[x][i], A[pos][i]);
		for(int i=x+1; i<=n; ++i) if(abs(A[i][y])>=eps) {
			double t=A[i][y]/A[x][y];
			for(int j=y; j<=m+1; ++j) A[i][j]-=t*A[x][j];
		}
		++x; ++y;
	}
	for(int i=x; i<=n; ++i) if(abs(A[i][m+1])>=eps) return 0;
	for(int i=m; i; --i) if(abs(A[i][i])>=eps) {
		for(int j=i+1; j<=m; ++j) A[i][m+1]-=A[j][m+1]*A[i][j];
		A[i][m+1]/=A[i][i];
	}
	return 1;
}
bool vis[N];
int n, m, X, Y, D, all;
void get(int now, double a[]) {
	if(!vis[now]) { a[now]=1; return; }
	int fx=1, x=now;
	if(x>n) fx=-1;
	for(int i=1; i<=m; ++i) {
		if(x==n) { fx=-1; x=all; }
		else if(x==n+1) { fx=1; x=1; }
		x+=fx;
		a[x]+=p[i];
	}
	a[now]-=1;
	a[all+1]=-C;
}
void bfs() {
	static int q[N], front, tail, x, fx;
	memset(vis, 0, sizeof(bool)*(all+1));
	front=tail=0; q[tail++]=X+D*n;
	while(front!=tail) {
		x=q[front++]; if(front==N) front=0;
		fx=1;
		if(x>n) fx=-1;
		for(int i=1; i<=m; ++i) {
			if(x==n) { fx=-1; x=all; }
			else if(x==n+1) { fx=1; x=1; }
			x+=fx;
			if(!vis[x] && abs(p[i])>=eps) { vis[x]=1; q[tail++]=x; if(tail==N) tail=0; }
		}
	}
	vis[1]=vis[all]=vis[Y]=vis[Y+n]=0;
}
int main() {
	int T; scanf("%d", &T);
	while(T--) {
		scanf("%d%d%d%d%d", &n, &m, &Y, &X, &D); ++X; ++Y;
		C=0;
		for(int i=1; i<=m; ++i) scanf("%lf", &p[i]), p[i]/=100, C+=p[i]*i;
		if(X==Y) { puts("0.00"); continue; }
		if(D==-1) { if(X==1) D=1; else D=0; }
		all=n<<1;
		bfs();
		for(int i=1; i<=all; ++i) { memset(A[i], 0, sizeof(double)*(all+2)); get(i, A[i]); }
		if(guass(all, all)) {
			if(abs(A[X+D*n][all+1])<eps) puts("Impossible !");
			else printf("%.2f
", A[X+D*n][all+1]);
		}
		else puts("Impossible !");
	}
	return 0;
}

  

这题我是调得憔悴啊QAQ

1、首先一开始我并没有想到有非法状态(即转移不到的状态我照样让他进了高斯中的矩阵中QAQ)

2、D=-1的条件漏看了啊,导致我sb的提交了20+次然后每次都是RE QAQ 妈妈呀...

首先方程和高斯消元比较显然..

设$d[i]$表示从$i$走到达终点所需要的步数:

$$d[i]=sum_{k=1}^{m} p[k](d[转移到k步后]+k)$$

变换一下变成:

$$sum_{k=1}^{m} p[k]d[转移到k步后] - d[i] = -sum_{k=1}^{m} p[k]*k$$

发现这和方向其实是有关的,那么我们可以变成两个状态:

$1<=i<=n$,$d[i]$表示从左向右,$d[i+n]$表示从右向左。判断一些非法状态即可

可是这样是有问题的,在某些情况下可能某个$i$没有合法被访问过,那么就要bfs一次....(因为概率为0的时候是不会访问的啊QAQ所以不要放入合法状态中...)

(至此概率期望dp完结撒花(咦为什么我做了12天才搞完?(妈妈呀,这效率太低了啊.

原文地址:https://www.cnblogs.com/iwtwiioi/p/4302522.html