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
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代碼。
我的部落格園:
我的CSDN部落格:https://blog.csdn.net/Charzous/article/details/107596506
————————————————
版權聲明:本文為CSDN部落客「Charzous」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/Charzous/article/details/107596506