部分内容来自:https://blog.csdn.net/a1097304791/article/details/83586769
题目描述:
给定一个被抓的概率P,银行的数量N,抢劫每个银行可得的金额m[i],以及抢劫每个银行会被抓概率p[i],每个银行可以选择抢劫也可以选择不抢劫,求访问完所有的银行后可以抢到的最大金额数,并使总的被抓的概率小于P。
本题是类似0-1背包的问题,可以用dp的方法解决。本题的一个关键是选择什么变量作为“容量”。由于题目中的概率精度未知,无法遍历,所以选择抢到的金额数作为“容量”。
令P'=1-P,表示访问完所有银行后安全逃脱的最小概率。
若令dp[i][j] 为访问第i个银行后抢到j元并安全逃脱的最大概率,则dp[N][j]就是访问完所有银行后抢到j元并安全逃脱的最大概率。由于dp[N][j]对应的是最安全的策略,若dp[N][j]<P',则没有一个更安全的方法能够抢到j元并逃脱。因此要找到一个最大的j,满足dp[N][j]>=P'
dp[i][j]的初始值要怎样设置呢?不难看出,没有访问银行,且没有抢任何钱,成功实现的概率是dp[0][0]=1;而没有访问银行,却抢到了钱,成功的概率是0,即dp[0][j]=0 (j>=1)
接下来就可以进行dp[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])
#include <bits/stdc++.h>
using namespace std;
const int maxn=105;
const double eps=1e-9;
int m[maxn];
double p[maxn];
double dp[maxn*maxn];
int sum;
int main()
{
#ifdef LOCAL_PC
freopen("E:/1.txt", "r", stdin);
#endif // LOCAL_PC
int T;
cin >> T;
while(T--)
{
double P;
int N;
sum=0;
cin >> P >> N;
P=1-P;
for(int i=1;i<=N;++i)
{
cin >> m[i] >> p[i];
p[i]=1-p[i];
sum+=m[i];
}
for(int i=1;i<=sum;++i) dp[i]=0;
dp[0]=1;
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)
{
cout << i << endl;
break;
}
}
}
return 0;
}