貌似是去年多校的题,很有营养
题目:http://acm.hdu.edu.cn/showproblem.php?pid=5819
题目意思:有n个骑士,他们如果方向相反,则会发生斗争,胜利的概率为1/2,现在问第N个骑士胜利的概率为多少,题目将1/2替换成500000004,答案%1e9+7拿到题目的第一时间想状态是什么。。然而并想不出来,因为dp功底菜。。
然后瞄了一眼其他人的。。
dp[i][j],代表,第i个位置的骑士,前面有j个向右走的概率
那么我们很容易可以推出状态转移方程
当这个骑士往右走的时候:
dp[i][j] = dp[i - 1][j - 1]
当这个骑士往左走的时候:
dp[i][j] = Σ(i - 1)(k = j) dp[i][k] * (1 / 2) ^ (k - j + 1)
这个表达式的时间复杂度为O(n^3),相对于题目的n = 1000是远远不够的,所以需要优化一下
可以观察到
dp[i][j + 1] = Σ(i - 1)(k = j + 1) dp[i][k] * (1 / 2) ^ (k - j)
跟dp[i][j]之差可得
dp[i][j + 1] - dp[i][j] = dp[i][j] - dp[i - 1][j];
化简得:
dp[i][j] = (dp[i][j + 1] + dp[i - 1][j]) / 2
当j = 1的时候。说明他打败了所有向右的骑士,最后向左移动
dp[i][1] = dp[i][2] + dp[i - 1][1];其实个人还是挺烦这种dp题的。。又要推状态。又要推改公式。最后还要考虑有没有特例。。。先涨一波姿势先。
附上代码:
/*
@resouces: hdu 5819
@date: 2017-3-16
@author: QuanQqqqq
@algorithm: 概率dp
dp[i][j] = dp[当前位置为i][向右的骑士还有j个]
当骑士向右的时候 dp[i][j] = dp[i - 1][j - 1]
当骑士向左的时候 dp[i][j] = Σ(i - 1)(k = j) dp[i][k] * (1 / 2) ^ (k - j + 1)
dp[i][j] = (dp[i][j + 1] + dp[i - 1][j]) / 2
*/
#include <bits/stdc++.h>
#define ll long long
#define MOD 1000000007LL
#define inv2 500000004LL
#define MAXN 1005
using namespace std;
ll dp[MAXN][MAXN];
int main(){
int T,stands,n;
scanf("%d",&T);
for(int t = 1;t <= T;t++){
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%d",&stands);
if(i == 1){
stands = 1;
}
if(i == n){
stands = 0;
}
if(stands){
for(int j = 1;j <= i;j++){
dp[i][j] = dp[i - 1][j - 1] % MOD;
}
} else {
for(int j = i - 1;j > 1;j--){
dp[i][j] = (dp[i][j + 1] + dp[i - 1][j]) % MOD * inv2 % MOD;
}
dp[i][1] = (dp[i][2] + dp[i - 1][1]) % MOD;
}
}
printf("Case #%d: %lld\n",t,dp[n][1] *inv2 % MOD);
}
}