Robberies HDU - 2955(DP+01背包+逆向思维+概率转换)

Robberies HDU - 2955

点击跳转↑

  • 题设:T为样例数,对于每个样例,给定P和N,分别表示小偷在偷了数个银行后,若被抓的总概率低于P他可以完美脱身,和一共可以偷的银行数量。接下来N行,每行两个值m[ i ]和p[ i ],分别代表一个银行可以偷到的钱,和偷了之后会被抓的概率(不同银行相互独立,总概率等于多个概率相乘)。问:在不被抓的情况下,最大能偷的金钱量。
  • 思路:很明显这是一个01背包问题,但是——
    01背包问题牵扯到两个基本的属性: 容量和价值, 如果直观的照搬01背包问题的话, 那么容量和价值应该是概率和钱数.
    但因为概率是一个浮点数, 而且题目也没有给定最小是几位小数(预计九位,将概率先扩大再照搬普通01背包,在遍历更新必超时), 所以无法遍历。
    那么就只能把容量定义为可得到的金钱, dp[ i ] 即为安全的概率, 题中给出的是被抓的概率, 但因为如果用被抓的概率在初始化上会有些麻烦, 所以干脆定义为不被抓, 也就是安全的概率反而比较简单.
    所以数据特殊处理:P=1-P,p【i】=1-p【i】。

状态转移方程和初始化:
(dp【v】表示偷得价值为 v 的金钱时不被抓的概率)

  • dp【0】= 1(表示价值为0时小偷必定不被抓)
  • dp【j】 = max( dp【j】 , dp【j-m【i】】 * p【i】 )

代码:

#include<bits/stdc++.h>
#define ll long long
#define SC(a) scanf("%d",&a)
#define mem(a,n) memset(a,n,sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
priority_queue <int,vector<int>,less<int> > QM;
const int INF= 0x3f3f3f3f;
const int maxn= 2e5+5;
const double eps= 1e-9;
int m[maxn],Mbag;
double p[maxn],dp[maxn];
int main()
{
	IOS;
	int t;
	cin>>t;
	while(t--)
	{
		double P,N;
		cin>>P>>N;
		//数据处理
		P = 1-P;
		Mbag=0;
		for(int i=1;i<=N;i++)
		{
			cin>>m[i]>>p[i];
			Mbag += m[i];
			p[i] = 1-p[i];
		}
		//初始化
		dp[0] = 1;
		for(int i=1;i<=Mbag;i++)   dp[i]=0;
		//01背包
		for(int i=1;i<=N;i++)
		{
			for(int j=Mbag;j>=m[i];j--)
				dp[j] = max(dp[j],dp[j-m[i]]*p[i]);
		}
		//逆向遍历取得最大结果
		for(int i=Mbag;i>=0;i--)
			if(dp[i]-P >= eps)//精度
			{
				cout<<i<<endl;
				break;
			}
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_45928596

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值