天天看點

hdu 2955 robberies(dp)

dp問題,注意兩個點

先回顧一下01背包模型

01背包模型裡的每個物品均有兩個量

1:每個物品的重量(代價)(價值一)

2:每個物品的價值(所得)(價值二)

特點

  • 1:限定某一價值(價值一),求另一價值(價值二)的最大值。(其中價值一與價值二的計算方式都是加和的關系(當然,依據不同的問題,計算方式上有可能不一樣,但總歸會有對某一價值的限定條件))
  • 2: 價值一與價值二都是離散的,不連續,可用數組的下标表示。

該題中,如果用01背包模型去類比,一開始預設機率是所付出的代價,而所槍的錢是收獲,事實其實也是這樣;但由于機率不是一個離散值,且被抓機率也不是簡單的和的關系,是以不能拿它當作角标,但所搶的錢數是一個離散值,且為簡單相加的關系是以将機率角标和數組中儲存的數交換位置;

是以狀态轉移方程中的狀态的定義就發生了變化。

于是動态轉移方程變為

f[ i ][ j ] = max{ f[ i-1 ][ j-m[ i ] ],f[ i-1 ][ j ] };

(i表示前i件物品,m[ i ]表示第i家銀行槍的錢,)

其狀态f[ i ][ j ]定義為搶前 i 家銀行且搶的錢為 j 時,所能逃跑的最大機率,(注意這裡有一個點:由于所給各家銀行的錢不一定能組合成從1到sum所有的值,針對這些值,最大逃跑機率均為0)

最後倒着輸出即可;

以下是AC代碼;

#include<iostream>
#include<cmath>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b)
using namespace std;
int m[105] = { 0 };
double p[105] = { 0 }, dp[105][10005];

int main()
{

	int t, i, j, n, sum;
	double maxprobability;
	cin >> t;
	while (t--)
	{
		sum = 0;
		memset(dp, 0, sizeof(dp));
		cin >> maxprobability >> n;
		for (i = 1; i <= n; i++)
		{
			cin >> m[i] >> p[i];
			p[i] = 1.0 - p[i];
		}
		for (i = 1; i <= n; i++)
			sum = sum + m[i];
		dp[0][0] = 1.0; //dp[0][1] = 1;
		for (i = 0; i <= n; i++)
			dp[i][0] = 1.0;

		for (i = 1; i <= n; i++)
		{
			for (j = 1; j <= sum; j++)
			{
				if (j >= m[i])
					dp[i][j] = max(dp[i - 1][j - m[i]] * p[i], dp[i - 1][j]);
				else
					dp[i][j] = dp[i - 1][j];
				//cout << dp[i][j]<<"=" << "F[" << i << " " << j <<"]" << "   ";
			}
			//cout << endl;
		}
		for (i = sum; i >= 0; i--)
		{
			if (dp[n][i] > 1.0 - maxprobability)
			{
				cout << i << endl;
				break;
			}
		}
	}
	//cin >> i;
	return 0;
}

           

确實要細細體會以下