LightOJ 1151 Snakes and Ladders(概率DP + 高斯消元)

时间:2023-12-04 15:04:38

题意:1~100的格子,有n个传送阵,一个把进入i的人瞬间传送到tp[i](可能传送到前面,也可能是后面),已知传送阵终点不会有另一个传送阵,1和100都不会有传送阵。每次走都需要掷一次骰子(1~6且可能性一样),掷多少走多少,目的地超出100重掷,问你走到100所需掷骰子的期望。

思路:概率DP肯定的,但是会往前传送就很难直接算。用DP[i]代表从i走到100的期望。

那么如果i没有传送阵,则有:DP[i] = 1 / 6 * sum(DP[i + j]) + 1,1<= j <= 6,如果超出100则有:DP[i] = 1 / 6 * sum((DP[i + j]) + DP[i] * k),k = min(100 - i,6),1<= j < k

有传送阵:DP[i] - DP[tp[i]] = 0

然后根据上式解出这个100元1次方程,用高斯消元

代码:

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn = + ;
const int MOD = 1e9 + ;
const int INF = 0x3f3f3f3f;
const double eps = 1e-;
double a[maxn][maxn], x[maxn];
int equ, var;
int Gauss(){
int i, j, k, col, max_r;
for(k = , col = ; k < equ && col < var; k++, col++){
max_r = k;
for(i = k + ; i < equ; i++)
if(fabs(a[i][col]) > fabs(a[max_r][col]))
max_r = i;
if(fabs(a[max_r][col]) < eps) return ;
if(k != max_r){
for(j = col; j < var; j++)
swap(a[k][j], a[max_r][j]);
swap(x[k], x[max_r]);
}
x[k] /= a[k][col];
for(j = col + ; j < var; j++) a[k][j] /= a[k][col];
a[k][col] = ;
for(i = ; i < equ; i++)
if(i != k){
x[i] -= x[k] * a[i][col];
for(j = col + ; j < var; j++) a[i][j] -= a[k][j] * a[i][col];
a[i][col] = ;
}
}
return ;
}
int tp[maxn];
int main(){
int t, ca = ;
scanf("%d", &t);
while(t--){
equ = var = ;
int n;
scanf("%d", &n);
memset(tp, -, sizeof(tp));
memset(a, , sizeof(a));
for(int i = ; i < n; i++){
int p;
scanf("%d", &p);
--p;
scanf("%d", &tp[p]);
--tp[p];
}
for(int i = ; i < ; i++){
if(tp[i] == -){
int k = min( - i, );
a[i][i] = ;
for(int j = i + ; j <= i + k; j++){
a[i][j] = -;
}
if(k < ) a[i][i] -= - k;
x[i] = ;
}
else{
a[i][i] = ;
a[i][tp[i]] = -;
x[i] = ;
}
}
Gauss();
printf("Case %d: %.8lf\n", ca++, x[]);
}
return ;
}