Robberies [hdu-2955]

題目鏈接:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章