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