1. 程式人生 > 其它 >(01揹包)hdu 2955 Robberies

(01揹包)hdu 2955 Robberies

技術標籤:動態規劃演算法

題目
hdu2955

題意:
給出 n 家銀行的金額和被抓的概率,求出不超出被抓概率 p 的最大搶劫金額。

思路:
如果把被抓概率看成揹包容量進行dp,則wa,
這裡犯了一個常識的錯誤,被抓概率 = 被抓概率1 + 被抓概率2,其實這是錯的。

舉個例子,某彩票的中獎概率是50%,按照上面的計算方法(中獎概率 = 中獎概率1 + 中獎概率2),那麼買兩張彩票則必中獎(兩張彩票中獎概率 = 50% + 50% = 100%),這是不科學的。

實際上兩張彩票中獎概率為75%(兩張彩票中獎概率 = 第一張中第二張不中 + 第一張不中第二張中 + 兩張都中 => 第一張中獎概率 * 第二張不中獎概率 + 第一張不中獎概率 * 第二張中獎概率 + 第一張中獎概率 * 第二張中獎概率 = 50% * 50% + 50% * 50% + 50% * 50% = 75%)

回到題目,也就是說,
被抓概率 = 第一家銀行被抓概率 * 第二家銀行不被抓概率 + 第一家銀行不被抓概率 * 第二家銀行被抓概率 + 第一家銀行被抓概率 * 第二家銀行被抓概率 => 1 - 第一家銀行不被抓概率 * 第二家銀行不被抓概率

因為被抓概率不是線性的,所有要把金額看成揹包容量進行dp,dp陣列記錄的是不被抓的最大概率。

程式碼

#include <bits/stdc++.h>
#define DEBUG freopen("_in.txt", "r", stdin); freopen("_out1.txt", "w", stdout);
#define CASE int t; cin >> t; while (t--) using namespace std; const int MAXN = 110; const int MAXM = 1e4 + 10; int v[MAXN]; double dp[MAXM], b[MAXN]; void solve(){ int n, tot = 0; double p; scanf("%lf%d", &p, &n); for (int i = 0; i < n; i++){ scanf("%d%lf", &
v[i], &b[i]); b[i] = 1 - b[i]; tot += v[i]; } memset(dp, 0, sizeof(dp)); dp[0] = 1; //一開始當然不會被抓 double temp; for (int i = 0; i < n; i++) for (int j = tot; j >= v[i]; j--) dp[j] = max(dp[j], dp[j-v[i]] * b[i]); for (int i = tot; i >= 0; i--) if (1 - dp[i] <= p){ //輸出不被抓的最大金額 printf("%d\n", i); break; } } int main(){ CASE solve(); return 0; }