一、概述
1. 問題描述
某同學要出國留學,共有資金n,學校m所,每所學校申請資金爲a[i],申請成功概率爲b[i],可以同時申請多所學校,某學校申請成功與否不會影響其他學校,求這位同學至少獲得一所學校offer的機率。
2. 問題鏈接
HDU -- I NEED A OFFER!(ACM Step: 3.3.6)
3. 問題截圖
圖1.1 問題截圖
二、算法思路
求至少獲得一個offer的概率 = 1 - (一個offer都得不到的概率)
這樣轉化的目的在於將問題轉換成了01揹包問題,設F[i,x]是資金x時申請前i所學校全部失敗的概率,此時,
1 - F[m,n]是問題所求,有轉移式:
F[i,x] = min(F[i-1,x],F[i-1,x-a[i]] x (1-b[i]))
這題有一個細節,就是正常輸入中m或者n有可能爲0,比如n=500,m=0。
在一開始的算法中爲了節省一點時間,我把最後一個學校單獨分析,只計算它在資金爲n時解答,而不去計算其他與問題最後結果無關的資金情況,後面發現正是由於上述的細節導致了算法不能通過,因爲c++沒有數組越界檢查,所以m-1可能會導致算法訪問到不合法的內存位置,修改前的代碼如下:
// for last element
if (schools[m-1].m <= n)
ans[n] = min2(ans[n], ans[n-schools[m-1].m]*schools[m-1].p);
意識到這個細節後,加上一個條件判斷就通過了,修改後的代碼如下:
// for last element
if (m>0 && schools[m-1].m <= n)
ans[n] = min2(ans[n], ans[n-schools[m-1].m]*schools[m-1].p);
三、算法實現
#include <iostream> // for cin, cout, endl
#include <cstdio> // for printf
using std::cin;
using std::cout;
using std::endl;
void input(int&);
double compute(int&, int&);
void output(double&);
const int MAX_NUM = 10000; // the max num of school
struct School
{
int m; // m for money
double p; // p for probability of failure
};
School schools[MAX_NUM]; // hold input
double ans[MAX_NUM+1]; // ans for answer, +1 for index from 0 to 1000
int main()
{
int m, n;
double res; // res for result
while (cin>>n && cin>>m && (m!=0 || n!=0)){
input(m);
res = compute(m, n);
output(res);
}
}
double min2(double a, double b)
{
if (a < b)
return a;
else
return b;
}
void input(int& m)
{
for (int i=0; i<m; ++i){
cin >> schools[i].m >> schools[i].p;
schools[i].p = 1.0 - schools[i].p;
}
}
double compute(int& m, int& n)
{
int i, j;
// initialize the elements of ans array to 1.0, indicate the probability of failure of first 0 school is 100%
for (i=0; i<=n; ++i)
ans[i] = 1.0;
// F[i, n] = min(F[i-1, n], F[i-1, n-m[i]]*p[i]
// for firest n-1 elements
for (i=0; i<m-1; ++i)
for (j=n; j>=schools[i].m; --j)
ans[j] = min2(ans[j], ans[j-schools[i].m]*schools[i].p);
// for last element
if (m>0 && schools[m-1].m <= n)
ans[n] = min2(ans[n], ans[n-schools[m-1].m]*schools[m-1].p);
return (1.0-ans[n])*100.0;
}
void output(double& res)
{
printf("%.1f%%\n", res);
}