9.2机房欢乐赛 礼物 (状态压缩+期望dp)

礼物

(gift.cpp\c\pas)

【问题描述】

夏川的生日就要到了。作为夏川形式上的男朋友,季堂打算给夏川买一些生日礼物。
商店里一共有种礼物。夏川每得到一种礼物,就会获得相应喜悦值Wi(每种礼物的喜悦值不能重复获得)。
每次,店员会按照一定的概率Pi(或者不拿出礼物),将第i种礼物拿出来。季堂每次都会将店员拿出来的礼物买下来。没有拿出来视为什么都没有买到,也算一次购买。
众所周知,白毛切开都是黑的。所以季堂希望最后夏川的喜悦值尽可能地高。
求夏川最后最大的喜悦值是多少,并求出使夏川得到这个喜悦值,季堂的期望购买次数。

【输入格式】

第一行,一个整数N,表示有N种礼物。
接下来N行,每行一个实数Pi和正整数Wi,表示第i种礼物被拿出来的概率和可以获得喜悦值。

【输出格式】

第一行,一个整数表示可以获得的最大喜悦值。
第二行,一个实数表示获得这个喜悦值的期望购买次数,保留3位小数。

【输入输出样例】

in
3
0.1 2
0.2 5
0.3 7
out
14
12.167

【数据范围】

对于10%的数据,N = 1
对于30%的数据,N ≤ 5
对于100%的数据,N ≤ 20 ,0 < Wi ≤ 10^9 ,0 < Pi ≤ 1且ΣPi ≤ 1
注意:本题不设spj

设f[i]表示拿到第n个物品后还需要多少次购买。
当第n个礼物也被拿完,就不需要购买任何礼物了,所以f[n]=0。
但是当拿到第n-1个礼物时,有p[n-1]的概率能直接拿到这个礼物;
而还有另外(1-p[n-1])的概率拿不到这个礼物。
但是每一次操作都视为一次购买,所以即使没有拿到这个礼物也依然要+1个次数。
这道题还有一点是礼物不是按顺序放出的,所以需要用状态压缩来保证所有情况的概率都能考虑到。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 30;
const int P = 1 << 20;

long long sum = 0;
int n, w, num[P + 5];
double p[N], dp[P + 5];

int lowbit(int x) {
    return x & (- x);
}

int count(int x) {
    int sum = 0;
    while(x) {
        sum ++;
        x -= lowbit(x);
    }
    return sum;
}

int main() {
    freopen("gift.in", "r", stdin);
    freopen("gift.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        scanf("%lf %d", &p[i], &w);
        sum += w;
    }
    if(n == 1)
        printf("%I64d\n%0.3lf", sum, 1.0 / p[1]);
    else {
        int cnt = (1 << n) - 1;
        for(int i = 0; i <= cnt; i ++)
            dp[i] = 0, num[i] = count(i);
        for(int k = n - 1; k >= 0; k --) {
            for(int i = 0; i <= cnt; i ++) {
                if(num[i] != k) continue;
                double ans = 0, tot = 1;
                for(int j = 0; j < n; j ++)
                    if(! (i & (1 << j))) {
                        tot -= p[j + 1];
                        ans += p[j + 1] * dp[i + (1 << j)];
                    }
                dp[i] = (ans + 1) / (1.0 - tot);
            }
        }
        printf("%I64d\n%0.3lf", sum, dp[0]); 
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值