Foreign Postcards Gym - 101190F(概率DP)

题目链接:https://vjudge.net/problem/Gym-101190F

题意:一开始共有n个卡片,C代表摆放正确,W代表摆放错误,每次可以选取前k张卡片,如果这k张卡片中的第一张是W,则将这k张卡片全部翻转后放在桌面上(翻转即C变W,W变C),如果第一张是C则不进行任何操作直接将这k张卡片放在桌面上,然后再在剩下的卡片中重复进行上述操作,直至所有卡片都放在桌面上了。求最终桌面上W个数的期望值。

思路:场上看出来是概率DP,但是不会推状态就去搞模拟了,看了题解的状态后自己推公式也能推出来,但是最难得还是如何找状态,DP学习很难一蹴而就,得经常联系培养思维才行。设P[i]为第i张牌作为k张牌中的第一张牌的可能性,p[0] = 1(第一张牌在第一次操作时一定是首张牌),p[i] = 1.0 / (n - i + 1.0),i >= 1。这个仔细退一下不难发现,我推到i=3发现的规律。设dp[i][0]代表第i张牌不翻转的概率,dp[i][1]代表第i张牌翻转的概率。最后对所有卡片为W的概率求和即可。如果s[i] == 'C', ans += dp[i][1](第i张卡片翻转后才能变成W), 否则ans += dp[i][0](第i张卡片起初就是W,故不需要翻转)。推状态转移方程时,只要考虑i和i-1即可。每张牌要么翻转要么不翻转,故dp[i][0] + dp[i][1] = 1,本来以为求出dp[i][0],直接用1-dp[i][0]求得dp[i][1]即可,但是可能是精度卡的太严的原因吧会Wrong Answer in Test 27,后来用状态转移公式求dp[i][1],就过了。看了RX的代码更加神奇,他写的用if else过不了,用三目运算符就能够。个人觉得是精度问题,但是我实在看不出他的两种写法有什么区别。RX的博客:http://blog.csdn.net/kim0403/article/details/77685142 dp状态转移方程详细见代码。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<sstream>
#include<deque>
#include<stack>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int  maxn = 1e6 + 20;
const int  maxt = 300 + 10;
const int mod = 10;
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, -1, 1};
const int Dis[] = {-1, 1, -5, 5};
const double inf = 0x3f3f3f3f;
const int MOD = 1000;
const double PI = acos(-1.0);
int n, m, k;
char s[maxn];
double dp[maxn][2];
double p[maxn];
int main(){
    freopen("foreign.in", "r", stdin);
    freopen("foreign.out", "w", stdout);
    scanf("%s", s);
    int len = strlen(s);
    p[0] = 1.0;//第一张牌一定会是第一次取的k张牌中的首张牌
    for(int i = 1; i < len; ++i) p[i] = 1.0 / (len - i + 1.0);//第i张牌是取出的k张牌的首张的概率
    if(s[0] == 'W'){//先确定好第一张牌翻转概率
        dp[0][0] = 0.0; dp[0][1] = 1.0;
    }
    else{
        dp[0][0] = 1.0; dp[0][1] = 0.0;
    }
    double ans = 0.0;
    for(int i = 1; i < len; ++i){
        if(s[i] == 'C'){
            dp[i][0] = p[i] + (1 - p[i]) * dp[i - 1][0];//i为第一张时不用翻转,i不是第一张第i-1张不用翻转时第i张也一定不用翻转
            dp[i][1] = (1 - p[i]) * dp[i - 1][1];//i不是第一张第i-1张翻转时第i张也一定也跟着翻转
//            dp[i][1] = 1.0 - dp[i][0];//这样求dp[i][1]会WA in Test 27
            ans += dp[i][1];
        }
        else{
            dp[i][0] = (1 - p[i]) * dp[i - 1][0];//i不是第一张第i-1张不用翻转时第i张也一定不用翻转
            dp[i][1] = p[i] + (1 - p[i]) * dp[i - 1][1];//i为第一张时会翻转,i不是第一张第i-1张翻转时第i张也一定跟着翻转
//            dp[i][1] = 1.0 - dp[i][0];
            ans += dp[i][0];
        }
    }
    printf("%.10f\n", ans);
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值