zoukankan      html  css  js  c++  java
  • [HDU4336]:Card Collector(概率DP)

    题目传送门


    题目描述

    夏川的生日就要到了。作为夏川形式上的男朋友,季堂打算给夏川买一些生日礼物。
    商店里一共有种礼物。夏川每得到一种礼物,就会获得相应喜悦值$W_i$(每种礼物的喜悦值不能重复获得)。
    每次,店员会按照一定的概率$P_i$(或者不拿出礼物),将第i种礼物拿出来。季堂每次都会将店员拿出来的礼物买下来。没有拿出来视为什么都没有买到,也算一次购买。
    众所周知,白毛切开都是黑的。所以季堂希望最后夏川的喜悦值尽可能地高。
    求夏川最后最大的喜悦值是多少,并求出使夏川得到这个喜悦值,季堂的期望购买次数。


    输入格式

    第一行,一个整数N,表示有N种礼物。
    接下来N行,每行一个实数$P_i$和正整数$W_i$,表示第i种礼物被拿出来的概率和可以获得喜悦值。


    输出格式

    第一行,一个整数表示可以获得的最大喜悦值。
    第二行,一个实数表示获得这个喜悦值的期望购买次数,保留3位小数。


    样例

    样例输出:

    3
    0.1 2
    0.2 5
    0.3 7

    样例输出:

    14
    12.167


    数据范围与提示

    对于10%的数据,N=1
    对于30%的数据,N≤5
    对于100%的数据,N≤20$0<{W}_{i}≤{10}^{9}$0<${P}_{i}$≤1且$sum limits_{i=1}^{n}{P}_{i}≤1$


    题意解释

    又是血淋淋的没看懂题,再次印证了得语文者得OI,售货员一次只可能拿出一个礼物,且每个礼物只能买一次(捂脸……)


    题解

    偷偷看了看数据范围,喜悦值没有负值,于是乎,第一问就解决了,肯定是把所有礼物的喜悦值加起来。

    那么来考虑10%的算法,N=1,我们只需要把这件礼物买下,且这件礼物的期望购买次数是$frac{1}{P_i}$

    再来考虑30%的算法,那么每种商品都有买或不买两种状态,那么共有$2^N$种状态,$2^{{N}^{3}}$刚好跑过。

    状态可以用状压(撞鸭)DP:设dp[S]表示当前状态为S,到买到所有礼物的期望步数。

    剩下的那个$N^3$算法很容易想到高斯消元。

    那么我们可以写出式子:

    $dp[S]=sum limits_{i=1}^N P_i imes dp[S']+(1-sum limits_{i=1}^N P_i) imes dp[S]+1$

    其中,S表示当前状态,S'为上一个状态,i表示没有买过的物品。

    写出这个式子,你就跟正解不远了,把式子移个项:

    $sum limits_{i=1}^N P_i imes dp[S]=sum limits_{i=1}^N P_i imes dp[S']+1\dp[S]=frac{sum limits_{i=1}^N P_i imes dp[S']+1}{sum limits_{i=1}^N P_i}$

    这就是我们的DP式子啦,时间复杂度$O(2^N)$

    注意HDU输入格式不同,而且是special judge,记得输出时多保留一位即可(跟HDU数据刚了一下午,才发现是special judge心态瞬间爆炸……)


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    double p[21];
    long long w[21];//记得开long long
    double dp[2000000];
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%lf%lld",&p[i],&w[i]);
    		p[0]+=p[i];
    		w[0]+=w[i];
    	}
    	printf("%lld
    ",w[0]);//第一问
    	for(int i=(1<<n)-2;i>=0;i--)//dp
    	{
    		double flag=0.0;//分母
    		for(int j=1;j<=n;j++)
    		{
    			if((1<<(j-1))&i)continue;
    			dp[i]+=p[j]*dp[i|1<<(j-1)];
    			flag+=p[j];
    		}
    		dp[i]=(dp[i]+1.0)/flag;
    	}
    	cout<<fixed<<setprecision(3)<<dp[0]<<endl;//保留小数输出第二问
    	return 0;
    }
    

    HDU代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    double p[21];
    double dp[2000000];
    int main()
    {
        while(~scanf("%lld",&n))
        {
            memset(dp,0,sizeof(dp));
            for(int i=1;i<=n;i++)
                scanf("%lf",&p[i]);
            for(int i=(1<<n)-2;i>=0;i--)
            {
                double flag=0.0;
                for(int j=1;j<=n;j++)
                {
                    if((1<<(j-1))&i)continue;
                    dp[i]+=p[j]*dp[i|1<<(j-1)];
                    flag+=p[j];
                }
                dp[i]=(dp[i]+1.0)/flag;
            }
            cout<<fixed<<setprecision(4)<<dp[0]<<endl;//注意HDU是special judge
        }
        return 0;
    }
    

    rp++

  • 相关阅读:
    平衡二叉查找树——AVL树
    Java 输入输出(一)——流
    C++获取系统当前时间(精确到微秒)
    C++ STL中哈希表 hash_map介绍
    ubuntu下面编译libuv
    linux使用select实现精确定时器详解
    .dll,.lib,.def 和 .exp文件
    没有core文件时候如何定位segment/core dump
    C++中string、char *、char[]的转换
    map自定义结构体作为key
  • 原文地址:https://www.cnblogs.com/wzc521/p/11194786.html
Copyright © 2011-2022 走看看