1. 程式人生 > >2013ACM-ICPC杭州賽區全國邀請賽——Random Walk

2013ACM-ICPC杭州賽區全國邀請賽——Random Walk

題目連結

  • 題意:
    n個點,按照題中給的公式可以求出任意兩個點轉移的概率。求從1到n的期望轉移次數
  • 分析:
    設dp[i]為從i到n的期望,那麼可以得到公式dp[i] = sigma(dp[i + j] * p(i + j, i)) + 1,1 <= j <= m
    把這個式子展開來:dp[i - m] * p(i - m, i) + dp[i - m + 1] * dp(i - m + 1, i) + ... + dp[i] * p(i, i) + ... + dp[i + m] * p(i + m, i) = dp[i]
    展開p(i, i),化簡:dp[i - m] * p(i - m, i) + dp[i - m + 1] * dp(i - m + 1, i) + ... + dp[i] * p‘(i, i) + ... + dp[i + m] * p(i + m, i) = -1(注意p'(i, i)和題目中有所不同了,等與p(i, i) - 1)
    其實這裡也可以發現,題目中的p(i, i)給的還是比較有特點的,有一個常數1,這樣在列方程的時候才可以消元使得方程右邊是一個常數
    解方程的時候,首先注意dp[n] = 0,這個方程是不用解的。之後可以安裝普通的gauss消元從上到下消元,再回代出結果;或者更簡單的,題目只要求dp[1],那麼如果從下到上求,最後直接除以係數即可

    這個題目的一個麻煩點在於對矩陣的下標處理:對於原始矩陣a[i][j],放到p[n][m]的矩陣中,就變成了p[i][m - i + j],所以對原矩陣進行消元的時候需要注意這一點
    再說一下這裡的處理:對於p[i][j],轉換過後就變成了a[i][m - i + j],也就是說,把a[i][i]變成p[i][m],這樣就方便儲存了

    也算是一個概率DP吧,比較關鍵的想法在於能將問題分解為n個狀態,之後就可以用高斯消元來解決了
    高斯消元的分析時,應該注意到這個矩陣比較稀疏,且消元的時候,只需要考慮最多m行的m個位置即可,複雜度不是普通的O(n ^ 3),而是O(n * m * m)
double b[maxn];
double p[maxn][15];

int main()
{
	//    freopen("in.txt", "r", stdin);
	while (~RII(n, m) && n)
	{
		FE(i, 1, n) FE(j, 1, m)
			RI(c[i][j]);
		FF(i, 1, n)
		{
			double sum = 1, s = 0;
			FE(j, 1, m)
				sum += c[i][j];
			FE(j, 1, m)
			{
				if (i - j >= 1)
					s += p[i][m - j] = 0.3 * c[i][j] / sum;
				if (i + j <= n)
					s += p[i][m + j] = 0.7 * c[i][j] / sum;
			}
			p[i][m] = -s;
			b[i] = -1;
		}
		FED(i, n - 1, 1)
		{
			int l = max(1, i - m), r = min(n - 1, i + m);
			FF(j, l, i)
			{
				double f = p[j][m - j + i] / p[i][m];
				FE(k, l, r)
					p[j][m - j + k] -= p[i][m - i + k] * f;
				b[j] -= f * b[i];
			}
		}
		printf("%.2f\n", b[1] / p[1][m]);
	}
	return 0;
}