【Contra】 矩阵乘法优化 dp

偶然间,chnlich发现了他小时候玩过的一个游戏“魂斗罗”,于是决定怀旧。但是这是一个奇怪的魂斗罗MOD。有N个关卡,初始有Q条命。每通过一个关卡,会得到u分和1条命,生命上限为Q。其中u=min(最近一次连续通过的关数,R)。若没有通过这个关卡,将会失去1条命,并进入下一个关卡。当没有生命或没有未挑战过的关卡时,游戏结束,得到的分数为每关得到的分数的总和。由于chnlich好久不玩这个游戏了,每条命通过每个关卡的概率均为p(0<=p<=1),原先chnlich的最高分纪录是S。现在chnlich想要知道,当p至少为多少时,chnlich期望获得的总分数能够超过原先的最高分。

矩阵乘法优化的dp。

g[i+1][min(j+1,R)][min(k+1),Q]+=g[i][j][k]*p
g[i+1][0][k-1]+=g[i][j][k]*(1-p)
Answer+=g[i][j][k]*p*(j+1)

#include 
#include 
#include 
#include 
#include 
#include 
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
using namespace std;
const int N = 40;
typedef long long LL;
typedef double Mtr[N][N];
int p[7][25], n, R, Q, S, m;
double d;
Mtr A, B, a, b;
void Mul(Mtr &a, Mtr b) {
	Mtr c;
	memset(c, 0, sizeof(c));
	Rep(i, 1, n)
		Rep(j, 1, n) 
			Rep(k, 1, n) c[i][j] += a[i][k] * b[k][j];
	Rep(i, 1, n) Rep(j, 1, n) a[i][j] = c[i][j];
}
bool Check(double P) {
	memset(a, 0, sizeof(a)), n = 0;
	memset(b, 0, sizeof(b));
	Rep(i, 0, Q) {
		int k = (i == Q) ? R : min(i-1, R);
		Rep(j, 0, max(k, 0)) p[i][j] = ++ n;
	}
	a[1][1] = 1;
	Rep(i, 1, Q) {
		int k = (i == Q) ? R : min(i-1, R);
		Rep(j, 0, max(k, 0)) {
			int x = p[i][j], y = p[i-1][0], z = p[min(i+1, Q)][min(j+1, R)];
			if (i > 1) a[x][y] = (1 - P);
			a[x][z] = P, a[x][1] = P * min((j + 1), R);
		}
	}
	Rep(i, 1, n) b[i][i] = 1;
	int y = m;
	while (y) {
		if (y & 1) Mul(b, a);
		Mul(a, a), y /= 2;
	}
	return b[p[Q][0]][1] > S;
}
int main()
{
	scanf ("%d%d%d%d", &m, &R, &Q, &S);
	int l = 0, r = 10000000, d = r;
	if (!Check(1)) { puts("Impossible."); return 0; }
	while (l < r) {
		int mid = (l + r) / 2;
		double p = double(mid) / d;
		if (Check(p)) r = mid;
		else l = mid + 1;
	}
	printf ("%.6f\n", double(l) / d);

    return 0;
}


你可能感兴趣的:(数学,tsinsen)