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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章