Robberies HDU - 2955 (01背包 + 概率转换)

01背包问题牵扯到两个基本的属性: 容量和价值, 如果直观的照搬01背包问题的话, 那么容量和价值应该是逃跑概率和钱数.

但因为概率是一个浮点数, 而且题目也没有给定最小是几位小数, 所以无法遍历

那么就只能把容量定义为可得到的金钱, dp[v] 即为安全的概率, 题中给出的是被抓的概率, 但因为如果用被抓的概率在初始化上会有些麻烦, 所以干脆定义为不被抓, 也就是安全的概率反而比较简单. 因为各个事件都是独立的, 所以多个事件安全的概率要用乘法相乘

可得状态转移方程

状态: dp[i][j]: 前i种物品中抢j元时不被抓的概率

dp[i][j] = max{dp[i-1][j], dp[i-1][j-m[i]] * p[i] | j >= m[i] }

优化一下空间, 即

dp[j] = max{dp[j], dp[j-m[i]] * p[i] | j >= m[i] } 

注意这里的P = 1-P


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const LL maxn = 10010;
const double eps = 1e-9;

int n, m[maxn], sum;
double P, dp[maxn], p[maxn];//抢到j元时不被抓的概率
int solve()
{
    ms(dp, 0);
    dp[0] = 1.0;
    for(int i = 1; i <= n; i++)
        for(int j = sum; j >= m[i]; j--)
            dp[j] = max(dp[j], dp[j-m[i]]*p[i]); //概率
    for(int i = sum; i >= 0; i--)
        if(dp[i] - P >= eps)
            return i;
}
int main()
{
    int T;
    cin >> T;
    while(T--){
        cin >> P >> n;
        sum = 0, P = 1.0-P; //sum为总价值, P为不被抓的概率
        for(int i = 1; i <= n; i++){
            cin >> m[i] >> p[i];
            sum += m[i], p[i] = 1.0-p[i]; //总价值和不被抓的概率
        }
        cout << solve() << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/83586769