DP 動態規劃 Problem V 1022 反向考慮的揹包

Heart.發表於2016-05-31

Problem V  ID:1022


簡單題意:某人準備搶銀行,可以承受的最大被抓的概率為p(總共),共有n個銀行可搶,分別給出各個銀行所擁有的money:mi,搶各個銀行被抓的概率pi。求可以搶到的最大金額。


解題思路形成過程:因為概率值的範圍為0~1,即有小數,所以必須反向來考慮。

            可以承受的最大被抓的概率為p,即:如果逃跑的概率大於1-p則符合要求。

            將所有銀行的總錢數作為揹包的容量,dp陣列各元素對應的值為逃跑的概率。

            如果搶兩個銀行i和j,則逃跑概率為(1-pi)*(1-pj),即兩個銀行逃跑的概率之積。(涉及到概率論)

            狀態轉移方程為:dp[j]=max(dp[j],dp[j-a[i]]*(1-b[i]));

            dp陣列中逃跑率符合要求且下標最大的一個所對應的下標即為所求答案。


感想:一開始被樣例給帶跑偏了,還以為會出現的概率是精確到小數點後兩位的,而實際上並不是。這就決定了很難用常用的揹包思想去解決這個問題。

    要學會去反向思考,要求是被抓的概率要符合要求,那逃跑的概率符合要求是否可以?dp陣列要存的元素能不能存經常存的下標?


程式碼:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
double dp[10001];
int a[101];
double b[101];
int main()
{
    //freopen("1.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        double p;
        int n,sum=0;
        scanf("%lf%d",&p,&n);
        for(int i=0; i<n; ++i)
        {
            scanf("%d%lf",&a[i],&b[i]);
            sum+=a[i];
        }
        dp[0]=1;
        for(int i=0; i<n; ++i)
            for(int j=sum; j>=a[i]; --j)
                dp[j]=max(dp[j],dp[j-a[i]]*(1-b[i]));
        for(int i=sum; i>=0; --i)
        {
            if(dp[i]>1-p)
            {
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;
}

相關文章