UVALive - 7527 Funfair (dp + 滚动)

  • 题目链接: UVALive - 7527 Funfair
  • 题意: 给定n个游戏,从中选取k种游戏去玩,玩家的金钱初始值为x. 假设玩第i个游戏之前,拥有的金钱为x0, 则在第i个游戏中若胜利,则金钱变成x0+ai, 否则金钱变成 (1-li)*x0. 问: 玩完k个游戏后,金钱最大值为多少.(即求期望最大值)
  • 给定, n, k, x
  • n行,第i行输入第i个游戏的ai, li, pi, (pi表示赢的概率)
  • 解法:
  • 假设当前金钱为x, 则玩完第i个游戏,金钱期望 = ( x + ai ) * p i + ( 1 - l i ) * x * ( 1 - pi );
  • 上述式子 = [ (1-li) * (1-pi) +pi ] * x + ai*pi;
  • 若仅在任意两个游戏之间考虑, 即考虑i, j. 则 可以通过先玩i再玩j 和 先玩j再玩i 两种可能的期望大小(将上述式子代入计算,可消除x的影响),确定i, j在最终选取的k个游戏中的先后顺序。故可先给游戏按照上述规则排序,再进行dp.
  • dp方程为: dp[i] = max( dp[i], [ (1-li) * (1-pi) +pi ] * dp[i-1] + ai*pi (1 < = i < = n)
  • dp[0] = x , 初始金钱数.
  • 利用滚动数组去求解,可降低空间复杂度.
  • 滚动数组原理: 由于dp方程中dp[i]仅与dp[i-1] (即上一次求解的结果)有关,假设我们已经执行完i-1下的情况, 那么此时dp[1 到 k]均属于i-1下求解的答案,而在i的情况, 每次从k -> 1, 这样保证每次的dp[i-1]均为上次求解结果,用此结果来更新当前的dp[i]。
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i=a; i<=b; ++i)
#define repp(i, b, a) for(int i=b; i>=a; --i)
using namespace std;
const int maxn = 1e2+7;
struct node{
    double a, l , p;
}g[maxn];
bool cmp(node a, node b){
    return a.a*a.p*b.l*(b.p-1) > b.a*b.p*a.l*(a.p-1); // 化简后的i, j比较式子
}
double dp[maxn];
int main(){
    double x;
    int n, k;
    while(~scanf("%d %d %lf", &n, &k, &x)){
        if(!n && !k && !x) break;
            rep(i, 1, n) {
            scanf("%lf %lf %lf", &g[i].a, &g[i].l, &g[i].p);
            g[i].l/=100.0;
            g[i].p/=100.0;
        }
        sort(g+1, g+1+n, cmp);
        memset(dp, 0, sizeof(dp));
        dp[0] = x;
        rep(i, 1, n){
            repp(j, k, 1){ //滚动数组求解
                dp[j] = max(dp[j], ((1-g[i].l)*(1-g[i].p)+g[i].p)*dp[j-1]+g[i].a*g[i].p);   
            }
        }
        printf("%.2lf\n", dp[k]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值