1. 程式人生 > >Robberies HDU - 2955 (01揹包 + 概率轉換)

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;
}