2020 年百度之星程式設計大賽 - 初賽三

Charzueus發表於2020-07-26

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\n",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\n";
        else
            cout<<"Yes\n";
    }
    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\n",nums);
    }
    return 0;
}

目前官方提示:
當 m≥⌊n/2⌋ 時,一定能變成 n...1,這時逆序對數最大。否則我們依次交換 1 和 n,2 和 n-1,.....,一共交換 m 對。
但是一個問題暫未解決,測試完全正確,而出現超時問題,可以看到我用數學求和公式代替迴圈,考慮可以解決問題,但是提交居然錯誤,還未找出原因。希望有大佬可以評論區提出方法,或者之後我解決了會更新AC程式碼。

我的部落格園: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

相關文章