• HDU4815 Little Tiger vs. Deep Monkey——0-1背包


    题目描述

    对于n道题目,每道题目有一个分值,答对加分,答错不得分,你要和一个叫深猴的比赛,题目你可以假设成判断题(不是对就是错),深猴对于所有的题目都是随机选择一个答案,而你是有脑子的,求为了不输掉比赛(平局或你获胜)的可能性至少为p时你至少需要得到多少分,有t组数据,每次输入两行,第一行为n,p(有n道题目,n<=40, 不会输的可能性为p,0.0<=p<=1.0),第二行输入n个1~1000的整数,代表这n道题分别答对能获得的分数

    样例输入

    1
    3 0.5
    1 2 3

    样例输出

    3

    题目分析

    首先对于这n道题目,深猴每次不是√就是×,那么答完所有的题目它的分数有2^n个结果(但是这其中可能会重复,比如三道题每题一分1 0 0和0 0 1其实得到的分数是一样的),而对于我们而言,我需要求出这n个题目自己得到每一种可能的分数的可能性,然后按分数从小到大将,这得到这些分数的概率相加,直到有一个分数m时,前面相加的概率和>=p,则在分数大于等于m时,确保我有至少p的概率不会输掉比赛

    错误示例

    我第一次做这道题目的时候想的通过递归计算出做出n个选择之后我可以得到的每一个分数的种数存放在a数组中,a[i]代表总分为i的种数,而又建立了一个辅助数组b[i]存放分数比i小的种数有多少种,很显然b[i] = b[i-1] + a[i-1](比i小的数量等于比i-1小的数量加上a[i-1]的数量),最后也是从小到大将每一种得分的可能性相加直到大于等于p时的分数则是答案,是一种前缀和的思想,但是会超时

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<string.h>
     4 #include<math.h>
     5 using namespace std;
     6 
     7 int k[45];
     8 int a[40005];
     9 int b[40005];
    10 int n;
    11 double p;
    12 
    13 void dfs(int i, int flag, int sum){            //第i个 选or不选 前一个状态的和 
    14     if(i > n){            //只有当所有的n个都作出了选择之后才能算是一种选择方案 
    15         a[sum]++;
    16         return; 
    17     }
    18     if(flag == 1){            //
    19         int next_sum = sum + k[i];
    20 //        a[next_sum]++;
    21         dfs(i+1, 1, next_sum);
    22         dfs(i+1, 0, next_sum);
    23     }else{                    //不选 
    24         int next_sum = sum;
    25 //        a[next_sum]++;
    26         dfs(i+1, 1, next_sum);
    27         dfs(i+1, 0, next_sum);
    28     }
    29 }
    30 
    31 void run(){
    32     //下一个的下标 选or不选 目前为止的和 
    33     dfs(1, 1, 0);
    34     dfs(1, 0, 0); 
    35 }
    36 
    37 void pre(){
    38     b[1] = 1;    
    39     int end = n*1000;                
    40     for(int i = 2; i <= end; i++){    //b[i]存放比i小的取法的数量 
    41         b[i] = b[i-1] + a[i-1];        
    42     }
    43 }
    44 
    45 void judge(){
    46     double m = 1;
    47     for(int i = 1; i <= n; i++){
    48         m *= 2;
    49     }    
    50     m *= p;
    51     long long x = ceil(m);
    52     int ans;
    53     int end = n*1000;        
    54     for(int i = 1; i <= end; i++){
    55         if(b[i] >= x){
    56             ans = i;
    57             break;
    58         } 
    59     } 
    60     printf("%d
    ", ans);
    61 }
    62 
    63 int main(){
    64     int t;
    65     scanf("%d", &t);
    66     while(t--){
    67         scanf("%d%lf", &n, &p);
    68         memset(a, 0, sizeof(a));
    69         memset(b, 0, sizeof(b));
    70         for(int i = 1; i <= n; i++) scanf("%d", &k[i]);
    71         run();
    72         pre();
    73         judge();
    74     }
    75     return 0;
    76 } 

    正确思路

    本题可以用到动态规划,0-1背包的思想,a[i]存放这n个题目的分数,dp[i][j]存放前i题,得到j分数的种数,而我们很显然可以想到,对于分数j,如果dp[i][j]可以得到,则他一定是dp[i-1][j-a[i]](第i题的分数取的种数) + dp[i-1][j](第i题的分数不取的种数)的基础上来的(对于第i题而言取的话,j == j - a[i] + a[i],不取的话就是i-1个问题,分数为j的种数,是0-1背包的问题),所以我们也可以对此用一个一维数组进行优化,dp[x]存放分数为x的种数,而初始化时dp[0] == 1,因为可以理解成前0题,得到0分的种数为1

    正确代码

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<math.h>
     4 #include<string.h>
     5 using namespace std;
     6 
     7 const int N = 40005;
     8 int a[45];
     9 double dp[N];
    10 int n;
    11 double p;
    12 
    13 int main(){
    14     int t;
    15     scanf("%d", &t);
    16     while(t--){
    17         scanf("%d%lf", &n, &p);
    18         int sum = 0;
    19         for(int i = 1;i <= n; i++){        //sum统计最大可以得到的分数 
    20             scanf("%d", &a[i]);
    21             sum += a[i];    
    22         } 
    23         memset(dp, 0, sizeof(dp));
    24         dp[0] = 1;                //前0题,得到0分的次数位1 
    25         for(int i = 1; i <= n; i++){            //类似于0-1背包的两个循环 
    26             for(int j = sum; j >= a[i]; j--){    
    27                 dp[j] += dp[j-a[i]];            //核心步骤,对于dp[j]而言,得分为j的种数是前i-1个时得分为j-a[i] 也就是取第i题,加上前i-1个时得分为j的种数 也就是不取第i题
    28             }
    29         } 
    30         double m = pow(2,n);        //统计所有的深猴的得分个数 
    31         int ans = 0;
    32         double ssum = 0;
    33         for(int i = 0; i <= sum; i++){
    34             dp[i] /= m;                //从小到大将概率累加直到大于等于p时的分数i就是答案,注意0也是一个得分 
    35             ssum += dp[i];
    36             if(ssum >= p){
    37                 ans = i;
    38                 break;
    39             }
    40         } 
    41         printf("%d
    ", ans);
    42     }
    43     return 0;
    44 }
    如果有任何意见请在评论区积极留言
  • 相关阅读:
    [MSSQL]也说SQL中显示星期几函数
    ECMAScript旮里旮旯儿一(galigalaoer)
    [MSQL]RANK函数
    敏捷背后的理论
    敏捷软件开发 Agile software Development
    第三章 WebGL资源 WebGL Resources
    第一章 WebGL简介 Introduction
    [MSSQL]PIVOT函数
    《Javascript高级程序设计》读书笔记 Number对象
    visual studio 2010 冷门技巧分享
  • 原文地址:https://www.cnblogs.com/YLTFY1998/p/11372951.html
Copyright © 2020-2023  润新知