HDU -- I NEED A OFFER!(ACM Step: 3.3.6)

一、概述

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);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值