題目鏈接:
http://acm.hdu.edu.cn/showproblem.php?pid=2955
可憐的POIUYTREWQ最近想買下dota2的商品,但是手頭缺錢。他想起了之前看過的一部大片,覺得搶銀行也許是個不錯的選擇。他認爲,壞人被抓是因爲沒有預先規劃。於是他在之前的幾個月對各大銀行進行了一次評估; 評估內容包括安全性和可盜竊金額: 他想知道在在某個風險係數下可以偷竊的最大金額
Input
第一行給出了一個整數T, 表示有T組測試數據. 對於每一組數據,第一行給出了一個浮點數P, 表示POIUYTREWQ允許被抓的最大概率, 和一個整數N,表示他計劃去搶劫的N個銀行. 接下來N行, 每行給出一個整數數Mj和浮點數Pj.
搶劫銀行 j 可獲得 Mj 百萬美金, 被抓的概率是 Pj .
Output
對於每組數據,每行輸出一個整數,表示POIUYTREWQ在被抓概率小於P的情況下,可搶到的最多的金錢。
Notes and Constraints
0 < T <= 100
0.0 <= P <= 1.0
0 < N <= 100
0 < Mj <= 100
0.0 <= Pj <= 1.0
你可以認爲每家銀行都是獨立的。
Sample Input
3
0.04 3
1 0.02
2 0.03
3 0.05
0.06 3
2 0.03
2 0.03
3 0.05
0.10 3
1 0.03
2 0.02
3 0.05
Sample Output
2
4
6
思路
要求在被抓概率小於p的情況下,所偷竊的最多金錢,
如果用dp[ i ] [ j ] 表示前 i 個銀行 ,概率不超過 j 的 情況下的最多金錢,發現 j 不是整數。
故 換個思路,求 前 i 個銀行,錢數不超過 j 的 情況下的 最小 被抓概率。
但是,仔細想想,被抓概率並不好算(大家仔細想想),而它的對立面,
不被抓概率就好算了,只要把選的每個銀行的不被抓概率相乘就行了,此時,
dp[ i ] [ j]表示前 i 個銀行,錢數不超過 j 的 情況下,不被抓概率的最大值。
而滿足的要求變爲,不被抓概率大於等於p的情況下,所偷竊的最多金錢。
題目中給定價值和被抓機率,但是被抓機率不可以用乘積來組合計算,舉個例子,
比如第一個銀行3%被抓機率,第二個5%被抓機率,那麼乘起來會變成0.15%,搶的
越多,被抓機率卻越小了,顯然不對,因此要轉換成不被抓機率,上述例子則變爲
第一家97%不被抓,第二家95%不被抓,乘起來就是92.15%,搶的越多,不被抓的
機率越來越小即被抓機率越來越大,這樣纔是符合常理的
//不被抓機率的最優(大)值,這根答案有什麼關係?逆序枚舉每一種情況,若此
情況下的dp值即不被抓機率大於等於題目中所給的不被抓機率,那就輸出,逆序着
從大到小枚舉保證了找到的一個解是最優解。
一.一維數組求解
#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxx=1e6+11;
int v[101];
double w[101];
double dp[maxx];
int main ()
{
int t;
cin >>t;
while(t--)
{
memset(dp,0,sizeof dp);
double p;
int n;
cin >>p>>n;
p=1-p;//題中給的最大被捉住概率,也就是給了最小逃跑概率1-p
int sum=0;
for(int i=0;i<n;i++)
{
cin >>v[i]>>w[i];
w[i]=1-w[i];
sum+=v[i];
}
dp[0]=1;//當啥也沒搶到沒偷時,逃跑的概率爲1
for(int i=0;i<n;i++)
for(int j=sum;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]*w[i]);//依次判斷搶這家銀行與不搶這家銀行那個逃跑的概率更高一點
for(int i=sum;i>=0;i--)
{
if(dp[i]>=p) //從搶的錢數最多開始,如果搶這麼多錢人還可以逃跑就輸出;
{
cout <<i<<endl;
break;
}
}
}
return 0;
}
二.二維數組求解
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
int v[106];
double m[106],dp[106][10006];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(v,0,sizeof(v));
memset(m,0,sizeof(m));
double k;
scanf("%lf",&k);
int n;
scanf("%d",&n);
int sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d %lf",&v[i],&m[i]);
sum+=v[i];
}
for(int i=0;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
dp[i][j]=1.0;
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
if(j>v[i])
dp[i][j]=min(dp[i-1][j],1-(1-dp[i-1][j-v[i]])*(1-m[i]));
else
{
dp[i][j]=min(dp[i-1][j],m[i]);
}
}
}
int ans=0;
for(int i=sum;i>=0;i--)
{
if(dp[n][i]<=k)
{
ans=i;break;
}
}
printf("%d\n",ans);
}
return 0;
}