HOJ 14121 Elections题解(带概率的01背包)

题面(下面有中文的,可以跳过):

Elections
Time Limit: 10000ms, Special Time Limit:25000ms, Memory Limit:524288KB
Total submit users: 9, Accepted users: 4
Problem 14121 : No special judgement
Problem description
Jenabkhan who has become billionaire from his Laboo bussiness, is now running for president. His country uses a strange mechanism, so-called electoral college, to select the president. There are several states in the country, and each state counts the votes independently. Depending on the population, each state has some members in the electoral college, and all of those members will vote the candidate with the majority of votes in their state. In the case of ties, each state has some tie-break rule to announce the clear winner. The president will be the candidate who receives more than half of votes in the electoral college.

Given the chance of Jenabkhan to win in each state, compute his winning probability in the electoral college. 

 
Input
The input consists of several test cases. Each test case starts with a line containing a single integer n denoting the number of states (1 ≤ n ≤ 1000). Each of the next n lines contains a real value pi with at most 4 digits after the decimal point (0 ≤ pi ≤ 1) and a positive integer ei, specifying the winning probability of Jenabkhan in the i-th state and the number of electoral votes associated with that state, respectively. The total number of members in the electoral college is an odd number and is no more than 2000. The input terminates with a line containing 0 which should not be processed. 

 
Output
For each test case, output in a single line containing the winning probability of Jenabkhan, rounded to exactly four digits after the decimal point (e.g., 0.3000 is correct while 0.3 is not). 

 
Sample Input
1
0.4 1
3
0.5 1
0.5 2
0.5 10
3
0.5 1
0.5 2
0.5 2
2
0.2 1
0.8 10
2
0.25 1
0.751 10
0
Sample Output
0.4000
0.5000
0.5000
0.8000
0.7510
Problem Source
Tehran 2016

 

题意:

题目抽象出来的模型就是:有n个物品,第i个物品有e[i]的重量,有p[i]的概率拿到第i个物品,问拿到物品重量是所有物品总重量一半以上的概率是多少~

总重量是不超过2000的奇数,最多1000个物品,一个物品要么取,要么不取,不允许取一部分~

思路:

因为那么多的东西,要么取,要去不取,那么显然是0-1背包,概率是价值,重量是代价。

下面考虑状态转移,一般在0-1背包里,我们设置dp[i][j]表示前i个物品,不超过j代价所能取得的最大价值。关于0-1背包,具体的见一维费用的背包问题,这里不多说了。同样,我们设置dp[i][j]表示前i个物品,取得j重量的概率——这时就没有所谓的最大概率,最小概率了,因为概率是固定的~

带概率的背包与普通的背包明显是不同的,毕竟多一个概率在那儿~这就意味着概率背包的状态转移和普通背包的状态转移是不一样的,普通背包的状态转移长这样:dp[i][j] = max(dp[i-1][j], dp[i-1][j-cost[i]] + value[i])。而对于这一题,主要有着两点区别:

  1. 普通背包如果不取第i个背包,那么dp[i][j] = dp[i-1][j],而概率dp即使不取第i个背包,二者也是不等的,因为在考虑前i-1个背包算概率的时候没有考虑第i个物品,没有考虑的意思就是不取第i个物品,但是不取第i个物品的概率是1-p[i],这个显然是需要乘上的
  2. 这一题不取最值而是取累加和,因为通过不同取法拿到j重量虽然各自概率都不同,但对于取到j重量都是由贡献的,需要加起来——这个实际上自己手动算概率的时候就会发现。

先处理一波:

dp[i][j] = dp[i-1][j] * (1-p[i])

然后状态转移:

dp[i][j] = \sum dp[i-1][j-e[i]] * p[i] / (1-p[i])

时间复杂度O(n^{2}) ,空间复杂度可以优化到O(\sum e ) ——dp数组的第一维可以像普通0-1背包一样去掉,p数组和e数组可以使用滚动数组优化到O(1)

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<iomanip>
#include<algorithm>
#include<string>
#include<cstring>
#include<ctime>
#include<queue>
#define ll long long
#define DEBUG printf("DEBUG\n")
#define FOR(i, s, n) for(int i = s; i < n; ++ i)
#define For(i, s, n) for(int i = s; i > n; -- i)
#define mem(a, n)    memset((a), (n), sizeof(a))

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
const int mod = 1e9+7;
const int maxn = 1e5+10;
using namespace std;
typedef vector<int> V;
typedef vector<V> VV;

double dp[2020];
int main()
{
    #ifdef AFei
    freopen("in.c","r",stdin);
    #endif // test
    int N;
    while(scanf("%d", &N), N)
    {
        memset(dp, 0, sizeof dp);
        dp[0] = 1;
        int sum = 0;
        int cnt = 0;
        for(int e, i = 0; i < N; ++ i)
        {
            double p;
            scanf("%lf%d", &p, &e);
            if(p == 1.0)
            {
                cnt += e;
                -- i;
                -- N;
                continue;
            }
            sum += e;
            FOR(j, 0, sum+1)    dp[j] *= 1-p;
            For(j, sum, e-1)    dp[j] += dp[j-e] * p / (1-p);    // 状态转移
        }

        double ans = 0;
        sum += cnt + 1;
        FOR(i, sum/2, sum+1)    ans +=  dp[i-cnt];
        printf("%.4f\n", ans);
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值