一、概述
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);
}