方程式を解くための確率期待値とガウスの消去法のための動的計画法

アルゴリズムクラスのプロジェクトには非常に興味深いトピックがあります。それは、動的計画法を使用して確率の期待値を見つけることです。これは、ガウスの消去法を使用して、これを記録します。

トピック:
リトルZは宝物を見つけるために古代の墓にやって来ました。古代の墓には多くの交差点と分岐道路があります。いくつかの交差点には罠があります。小さなZは交差点iで罠を通過するたびに、A [i]の血を落とし、罠は永続的です(つまり、小さなZが到着A [i]ポイントの血液が交差点i)にドロップされます。幸いなことに、トラップのない交差点がいくつかあります。残念ながら、Xiao Zは道路の馬鹿であり、どこに行ったか、どこに行くかを判断する方法がありません。次の交差点に到達するために、各交差点の分岐点までランダムに(等しく)歩くことしかできません。リトルZは現在、古代の墓の入り口(つまり、ジャンクション1)にあり、ここにはトラップはありません。宝物はジャンクションnに隠されており、トラップはありません。

あなたが予期していなかったことは、あなたがこの古代の墓の守護者であるということです。この古代の墓のすべての構造(交差点、フォーク、トラップを含む)を知っています。次に、小さなZが宝物を生きているのを見ることができる確率を計算する必要があります。

この関数のインターフェースは次のようになります。

double func(int n, int hp, vector<int>& damage, vector<int>& edges) {
    
    
}

その中で、パラメータnは交差の数、hpは小さいZの初期血液量、アレイの損傷はn個のデータであり、n個の交差トラップの損傷を表します(トラップがないことを確認するために、トラップは0ではありません)。ジャンクション1とnで);データエッジは2 *(フォークの数)データであり、2データごとにエッジであり、エッジは双方向です。

例:
三角形の交差点1、2、3があります。リトルZには2ポイントの血があります。小さいZは1にあり、宝物は3にあり、トラップは2にあります(損傷は1です)。その結果は0.875です。リトルZは、HPが0になる前に3に達すると成功するため、失敗への唯一のパスは1-2-1-2です。道を見つけるたびにランダムになり、3回失敗する確率は0.5 ^です。 3 = 0.125の場合、3に正常に到達する確率は0.875です。
ここに画像の説明を挿入します
入力例:

3 2 3
0 1 0
1 2 
1 3
2 3

n = 3、hp = 2、3つのエッジがあり、damage [] = {0,1,0}、エッジは1-> 2,1-> 3,2-> 3、edges [] = {1、 2,1,3,2,3}。

アイデア:
2次元配列dp [hp + 1] [n + 1]を作成します。ここで、dp [i] [j]は、i滴の血液が残ったときに点jに到達する予想回数を表します。状態遷移方程式をリストします
ここに画像の説明を挿入します
。i= hp、j = 1の場合、開始点がノード1であるため、1を追加します。これにより、必然的にノード1を1回通過します。終わりを除外するということは、終わりに達すると終わりになるので、終わりが戻ったとは見なされないということです。

損傷[j]> 0の場合、下層を定数と見なすと、dp [i] [j]を簡単に計算できます。Damage [j] = 0の場合、このhpレイヤーでダメージが0であるすべての交差点のdp値には関係があり(つまり、方程式)、値を直接計算することはできません。最初に、このレベルで損傷が0でない交差点のdp値を見つけ、それらを定数として扱い、次にガウスの消去法を使用して、損傷= 0の交差点dpの線形方程式を解きます。このhpレベルのすべての交差点。dp値。このレイヤーが見つかったら、前のレイヤーの解決を続けることができます。

最後に、Xiao Zが宝物を生きていると見ることができる確率は、
ここに画像の説明を挿入します
hp> = 1のときに交差点nに到達するために予想される回数の合計です。

コード:

//选择列主元并进行消元
void upperTrangle(vector<vector<double>> &a,int n) {
    
    
	double tmp; //用于记录消元时的因数
	for (int i = 1; i <= n; i++) {
    
    
		int r = i;
		for (int j = i + 1; j <= n; j++)
			if (fabs(a[j][i]) > fabs(a[r][i]))
				r = j;
		if (r != i)
			for (int j = i; j <= n + 1; j++)
				swap(a[i][j], a[r][j]);//与最大主元所在行交换
		for (int j = i + 1; j <= n; j++) {
    
    //消元
			tmp = a[j][i] / a[i][i];
			for (int k = i; k <= n + 1; k++)
				a[j][k] -= a[i][k] * tmp;
		}
	}
}
//高斯消元法(列选主元)
void Gauss(vector<vector<double>> &a, int n) {
    
    
	upperTrangle(a, n);//列选主元并消元成上三角

	for (int i = n; i >= 1; i--) {
    
    //回代求解
		for (int j = i + 1; j <= n; j++)
			a[i][n + 1] -= a[i][j] * a[j][n + 1];
		a[i][n + 1] /= a[i][i];
	}
}

vector<int> findAdjacent(vector<int> edges, int p) {
    
    //找p点的相邻点
	vector<int> points;
	for (int i = 0; i < edges.size() / 2; ++i) {
    
    
		if (edges[2 * i] == p) {
    
    
			points.push_back(edges[2 * i + 1]);
		}
		else if (edges[2 * i + 1] == p) {
    
    
			points.push_back(edges[2 * i]);
		}
	}
	return points;
}

double func(int n, int hp, vector<int>& damage, vector<int>& edges) {
    
    
	vector<vector<double>> dp;
	for (int i = 0; i < hp + 1; ++i) {
    
    
		vector<double> tmp;
		for (int j = 0; j < n + 1; ++j) {
    
    
			tmp.push_back(0);
		}
		dp.push_back(tmp);
	}
	dp[hp][1] = 1;

	vector<int> adjacentCount;//邻接点个数,下标是点标识
	for(int i=0;i<=n;++i){
    
    
		if(i==0) adjacentCount.push_back(0);
		else adjacentCount.push_back(findAdjacent(edges,i).size());
	}

	for (int row = hp; row >= 1; --row) {
    
    
		//先计算damage不为0的点
		for (int col = 1; col <= n; ++col) {
    
    
			if (damage[col - 1] > 0) {
    
    
				for (int i : findAdjacent(edges, col)) {
    
    //不为终点的相邻点
					if (i != n && row + damage[col - 1] <= hp) {
    
    
						dp[row][col] += dp[row + damage[col - 1]][i]/(double)adjacentCount[i];
					}
				}
			}
		}
	    
		//计算damage为0的点
		vector<int> zero;
		vector<vector<double>> matrix;//增广矩阵的扩大

		for (int col = 1; col <= n; ++col) {
    
    
			if (damage[col - 1] == 0) {
    
    
				zero.push_back(col);
			}
		}

		for (int i = 0; i < zero.size() + 1; ++i) {
    
    //矩阵n+1行 n+2列 第1行第1列均为0 其余部分为增广矩阵
			vector<double> tmp;
			for (int j = 0; j < zero.size() + 2; ++j) {
    
    
				tmp.push_back(0);
			}
			matrix.push_back(tmp);
		}
 

		//填充增广矩阵
		for (int i = 0;i<zero.size();++i){
    
    
			matrix[i + 1][i + 1] = -1;
			matrix[i+1][zero.size() + 1] = -dp[row][zero[i]];//常数项
			
			for (int k : findAdjacent(edges, zero[i])) {
    
    
				if (k != n) {
    
    

					if (damage[k-1] > 0) {
    
    //若damage>0,则为常数项
						matrix[i+1][zero.size() + 1] -= dp[row][k]/(double)adjacentCount[k];
					}
					else {
    
    //若damage=0,则为未知项
						for (int index = 0; index < zero.size(); ++index) {
    
    
							if (zero[index] == k) {
    
    
								matrix[i+1][index + 1] = 1/(double)adjacentCount[k];
								break;
							}
						}
					}
				}
			}
			
		}

		//高斯消元法求解
		Gauss(matrix, zero.size());

		//将解写回dp中
		for (int i = 0; i < zero.size(); ++i) {
    
    
			dp[row][zero[i]] = matrix[i + 1][zero.size() + 1];
		}
	}


	double result = 0;
	for (int i = 1; i <= hp; ++i) {
    
    
		result += dp[i][n];
	}

	return result;
}

時間計算量はO(hp * n ^ 2)であり、空間計算量はO(hp * n)です。

おすすめ

転載: blog.csdn.net/livingsu/article/details/106824702