【概率DP】hdu 5819

貌似是去年多校的题,很有营养

题目: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 

#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);
    }
}

你可能感兴趣的:(概率DP,dp)