礼物
(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;
}