• 2020 年百度之星程序设计大赛


    2020 年百度之星·程序设计大赛 - 初赛三解题思路及代码(Discount、Game、Permutation)

    1、Discount

    Problem Description
    学皇来到了一个餐馆吃饭。他觉得这家餐馆很好吃,于是就想办个会员。
    一共有 nn 种会员充值卡套餐,假设学皇这餐饭的消费为 a 元,选择第 ii 种套餐,需要充值 b[i] * a 的钱,这次吃饭可以打 c[i]×10 折,由充值的钱支付(即这次吃饭只需要从充值金额中扣除 a×c[i] 元)。以后用剩余的充值的钱吃饭不再打折。
    请问学皇应该选择哪个套餐(必须选择恰好一个套餐),使得优惠的比例最大?
    优惠比例的定义是把充的钱用完以后,(本来应该付的钱 - 实际付的钱) / 本来应该付的钱。在这个题目里,实际付的钱就是这次充值的花费。

    Input
    第一行一个整数 test(1≤test≤100) 表示数据组数。
    对于每组数据,第一行一个正整数 n(1 leq n leq 100)n(1≤n≤100) 表示套餐的数目。
    接下来 nn 行,每行一个正整数 b[i] (1≤b[i]≤100) 和一个小数 c[i](0≤c[i]≤1,c[i] 最多包含两位小数)。
    Output
    对于每组数据,输出一个五位小数表示最大的优惠比例。如果小数点后超过五位,四舍五入到五位。

    Sample Input
    1
    2
    2 0.5
    3 0.1
    Sample Output
    0.23077
    样例解释
    对于第一种套餐,优惠比例为 0.5a / (2a + 0.5a) = 0.2;
    对于第二种套餐,优惠比例为 0.9a / (3a + 0.9a) = 9 / 39;

    阅读题目,会感觉有点复杂,而且一些表达并不好理解,而这道题关键在于Sample Output,它给出了样例的解释说明,直观可以判断出题目最大优惠比例的计算方式,后续很容易解出题目。

    Accept代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int test,i,j,n;
        float c[105],b[105];
        scanf("%d",&test);
        while(test--)
        {
            scanf("%d",&n);
            float r1=0.0,r=0.0;
            for(i=0;i<n;i++)
                scanf("%f%f",&b[i],&c[i]);
            for(i=0;i<n;i++)
            {
                if((1-c[i])/(b[i]+1-c[i])>r)//计算每种方案的优惠比例,取最大值
                    r=(1-c[i])/(b[i]+1-c[i]);
            }
            printf("%.5f
    ",r);    
        }
        return 0;
    } 

    2、Game

    Problem Description
    Alice 和 Bob 在玩游戏。
    桌面上有两堆金币,少的那堆有 x 个金币,多的那堆有 2x 个金币。
    假设金币可以被无限细分。Alice 和 Bob 事先都不知道 x 是几,但是他们都知道 x 是一个 (0,1] 之间均匀分布的随机实数。
    Alice 会等概率的被分配到其中的一堆金币,Bob 会得到另一堆。xx 的值和两堆金币的分配是相互独立的。
    拿到金币以后,Alice 会马上数清自己拿到多少金币。然后 Alice 可以选择是否和 Bob 那堆换。
    给定 Alice 拿到的金币数目,请问 Alice 要不要交换,使得她期望能得到的金币数目更多?
    如果交换期望得到的金币数目多于不交换期望得到的金币数目,输出交换,否则不交换。

    Input
    第一行一个正整数 test (1≤test≤200000) 表示数据组数。
    接下来每行一个小数 p (0<p≤2),p 最多保留五位小数,表示 Alice 拿到的金币数目。
    Output
    对于每组数据,输出 Yes 表示需要交换,输出 No 表示不要交换。

    Sample Input
    1
    1.00000
    Sample Output
    Yes

    先看了题目,差点被误导为难题,其实看了程序太简单了,实际理解起来类似于博弈问题,当Alice在游戏中要获得更多期望金币,必须从概率方面进行预测。
    简而言之,当p>1时,Alice一定就是得到最大那堆金币了,所以一定不交换,因为p=2*x,x 是一个 (0,1] 之间均匀分布的随机实数;反之,当p<=1,存在Bob可能获得最大金币,所以Alice选择交换可能获得最大金币。
    虽然存在Alice拿到0.8,Bob拿到0.4或者1.6,但是还是交换为好,碰运气。

    目前官方提示: 当 p > 1 时,alice 拿到的一定是大的那部分,所以一定不换; 否则,alice 交换以后有一半概率翻倍,一般概率减半,0.25+0.50.5=1.25,所以一定换。

    Accept代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int test;
        float p;
        cin>>test;
        while(test--)
        {
            cin>>p;
            if(p>1.0)
                cout<<"No
    ";
            else
                cout<<"Yes
    ";
        }
        return 0;
    }

    3、Permutation

    Problem Description
    一开始有 n 个数,他们按 1…n 的顺序排列,要求交换最多 m 对数字(同一个数字可以参与多次交换),使得逆序对数目最大。
    对于一个序列 A,如果存在正整数 i,j 使得 1≤i<j≤n 而且 A[i] > A[j]A[i]>A[j],则 <A[i],A[j]> 这个有序对称为 A 的一个逆序对。

    Input
    第一行一个正整数test (1≤test≤100000) 表示数据组数。
    对于每组数据,一行两个整数 n,m (1≤n≤1000000,0≤m≤1000000) 表示数字个数和最多可以交换的数字对数。
    Output
    对于每组数据,一行一个整数表示答案。

    Sample Input
    6
    1 1
    2 0
    2 1
    3 1
    4 1
    4 2
    Sample Output
    0
    0
    1
    3
    5
    6

    看到题目,在纸上写几个简单样例,就很容易发现这是一个找数学规律的题目,可推导出3种情况,具体见代码理解不难。

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int test,n,m,i;
        scanf("%d",&test);
        while(test--)
        {
            scanf("%d%d",&n,&m);
            int nums=0;
            if(n==1||m==0)
                nums=0;
            else if(m>=n/2)//当交换次数>=数值向下取整的一半,退化为1+2+……+n-1之和
            {
    //            for(i=1;i<=n-1;i++)
    //                nums+=i;
                    nums=n*(n-1)/2;//数学求和公式代替循环
            }
            else
            {
                int minum=n-2*m;//此题关键,推导一般的情况,得到的逆序对
    //            for(i=minum;i<=n-1;i++)
    //                nums+=i;
                    nums=(minum+n-1)*(n-minum)/2;//数学求和公式代替循环
            }
            printf("%d
    ",nums);
        }
        return 0;
    }

    目前官方提示:
    当 m≥⌊n/2⌋ 时,一定能变成 n...1,这时逆序对数最大。否则我们依次交换 1 和 n,2 和 n-1,.....,一共交换 m 对。
    但是一个问题暂未解决,测试完全正确,而出现超时问题,可以看到我用数学求和公式代替循环,考虑可以解决问题,但是提交居然错误,还未找出原因。希望有大佬可以评论区提出方法,或者之后我解决了会更新AC代码。


    第三题更新啦,debug真实令人……,上面我的想法没错,可用数学公式求和来提高计算效率,提交为何错误呢?我找到了,尝试一个1000000的值,即n=1000000,m=500000可知,定义的变量越界了,所以需要都是长整型long long,小马虎让我不能AC。
    所以上面代码稍微修改几个变量类型就OK。
    Accept代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int main()
    {
        ll test,n,m,i;
        scanf("%lld",&test);
        while(test--)
        {
            scanf("%lld%lld",&n,&m);
            ll nums=0;
            if(n==1||m==0)
                nums=0;
            else if(m>=n/2)//当交换次数>=数值向下取整的一半,退化为1+2+……+n-1之和
                nums=n*(n-1)/2;//数学求和公式代替循环,提高效率
            else
            {
                ll minum=n-2*m;//此题关键,推导一般情况公式,得到的逆序对
                nums=(minum+n-1)*(n-minum)/2;//数学求和公式代替循环,提高效率
            }
            printf("%lld
    ",nums);
        }
        return 0;
    }

    其他方法大家可以参考我的CSDN文章评论区小伙伴提出来的哦。

    我的博客园:https://www.cnblogs.com/chenzhenhong/p/13381296.html
    我的CSDN博客:https://blog.csdn.net/Charzous/article/details/107596506

    ————————————————
    版权声明:本文为CSDN博主「Charzous」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Charzous/article/details/107596506

  • 相关阅读:
    Java 对象初始化
    Java 栈和堆
    值得细品
    磁盘的分区、格式化与挂载
    VirtualBox预存空间不足
    做个备忘
    SQL查数据库有哪些触发器,存储过程...
    SQL 中 CASE
    FMX的Style中的Effects的注意问题
    Python图像处理库(2)
  • 原文地址:https://www.cnblogs.com/chenzhenhong/p/13381296.html
Copyright © 2020-2023  润新知