2020 年百度之星程式設計大賽初賽二部分題解
弱雞隻做出來五題。。
Poker
傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=6775
題意
小沃沃在玩一個有趣的遊戲。
初始他有 \(n\) 塊錢,每一輪他需要投入至少 \(m\) 塊錢,系統會拿走其中 \(p\%\) 的錢,並把剩下的錢還給他。
請問在最優情況下,小沃沃最多可以玩多少輪?
假設當前一輪小沃沃投入了 \(x\) 塊錢,那麼他可以收回$ ⌊x\times (100−p)/100 ⌋ $塊錢,其中 \(⌊a⌋\) 表示 \(a\) 取下整。
小沃沃每一輪投入的錢不能超過他現在擁有的錢。
每一輪投入的錢必須為整數。
分析
那就是每一次就丟 \(m\)
程式碼
#include <bits/stdc++.h> using namespace std; int main(){ int t; scanf("%d",&t); while(t--){ int n,m,p; scanf("%d%d%d",&n,&m,&p); if(n<m){ printf("0\n"); continue; } int x=ceil(m*p*1.0/100); //返回的錢是向下取整,那丟出去的錢就是想上取整 printf("%d\n",(n-m)/x+1); } return 0; }
Distance
傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=6776
題意
給出一堆點到固定點的距離,問所有給出的點兩兩之間距離之和。
分析
自然是把所有點放到一條線上和最短。
但是題目資料範圍是 \(10^5\) 用 \(O(n^2)\)的方法是不行的。
我們可以用字首和解決這個題目。把所有的距離按從小到大排好,用 \(arr\) 陣列存放資料,用 \(sum\) 陣列記錄字首和。
我們不需要重複記錄兩個點之間的距離,所有我們假定從最大的開始計算。
\[\sum_{i=1}^{n-1}dis[n,i]=arr[n]\times(n-1)-sum[n-1] \]
這樣我們就可以用\(O(n)\)複雜度實現了。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
long long arr[MAXN];
long long sum[MAXN];
int n;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",arr+i);
sort(arr+1,arr+1+n);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+arr[i];
long long res=0;
for(int i=n;i;i--)res+=(i-1)*arr[i]-sum[i-1];
printf("%lld\n",res);
}
return 0;
}
Covid
傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=6777
題意
有 \(n\) 個人,會在不同時刻出現在不同位置,其中 \(1\) 號人物是感染者,在同一時間同一地點一起出現的人會被感染者感染,問最後哪些人感染了。
分析
我們可以用時間作為第一關鍵字,地點作為第二關鍵字排序,在迴圈一遍看看哪些人是在同一時間同一地點出現了,把這些人存放在一個\(vector\) 裡面,如果出現了感染者就全部感染,否則就清空。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int MAXM=2e5+10;
const int MAXN=2e4+10;
int vis[MAXN];
struct node{
int t,p;
int id;
bool operator<(const node a)const {
return t==a.t?p<a.p:t<a.t;
//以時間為第一關鍵字,地點為第二關鍵字排序
}
}arr[MAXM];
int cnt;
int n;
vector<int>tem;
int main(){
int t;
scanf("%d",&t);
while(t--){
cnt=1;
scanf("%d",&n);
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++){
int m;
scanf("%d",&m);
while(m--){
scanf("%d%d",&arr[cnt].t,&arr[cnt].p);
arr[cnt].id=i;
cnt++;
}
}
arr[0].t=0,arr[0].p=0,arr[0].id=1;
sort(arr,arr+cnt);
tem.clear();
int f=0;
vis[1]=1;
for(int i=1;i<cnt;i++){
if(arr[i].t==arr[i-1].t&&arr[i].p==arr[i-1].p)tem.push_back(arr[i].id);
//同一時間同一地點的人加到tem
else {
if(f){
//存在感染者則全部感染
for(auto x:tem)vis[x]=1;
f=0;
}
tem.clear();//清空
tem.push_back(arr[i].id);
}
if(vis[arr[i].id])f=1;
}
if(f)for(auto x:tem)vis[x]=1;
tem.clear();
for(int i=1;i<=n;i++)
if(vis[i])tem.push_back(i);
//因為最後不能有空格,比賽的時候沒咋考慮寫法就寫成這樣了,其實就是輸出
for(int i=0;i<tem.size();i++){
if(i==tem.size()-1)printf("%d",tem[i]);
else printf("%d ",tem[i]);
}
printf("\n");
}
return 0;
}
Car
傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=6778
題意
給一堆車牌,可以對最後一位進行限制通行,每個尾號最多限制一次,問五天內哪種限制方法的單日最高可通行車輛數最少。
分析
發現尾號最多就 \(0-9\) 就這麼十個數字,那對於每天限制哪些我們可以通過全排列列舉。
但是另一個限制就是,怎麼分配每天限制的尾號數量,畢竟不能一天把所有尾號都限行或者平均分。
每天分配的限制其實就只有7種情況。
週一 | 週二 | 週三 | 週四 | 週五 |
---|---|---|---|---|
1 | 1 | 1 | 1 | 6 |
1 | 1 | 1 | 2 | 5 |
1 | 1 | 1 | 3 | 4 |
1 | 1 | 2 | 2 | 4 |
1 | 1 | 2 | 3 | 3 |
1 | 2 | 2 | 2 | 3 |
2 | 2 | 2 | 2 | 2 |
那麼我們在全排列的時候分別求這其中情況下的最值就行了。
注意,對於週一限制尾號\(1\),週二限制尾號\(2\),週三限制尾號\(3\),週四限制尾號\(4\),週五限制尾號\(5,6,7,8,9,0\),和週一限制尾號\(5,6,7,8,9,0\),週二限制尾號\(2\),週三限制尾號\(3\),週四限制尾號\(4\),週五限制尾號\(1\),應當看成一種情況,所以只存在上面的7種情況。最後900ms壓線過。
離譜程式碼
感覺裡面套個迴圈有點不好寫就直接。。。
#include <bits/stdc++.h>
using namespace std;
int arr[10]={0,1,2,3,4,5,6,7,8,9};
int sum[15];
int n;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(sum,0,sizeof sum);
char str[10];
for(int i=0;i<n;i++)scanf("%s",str),sum[str[4]-'0']++;
int res=1e9;
do{
int ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]);
ans=max(ans,n-sum[arr[4]]-sum[arr[5]]-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]-sum[arr[4]]);
ans=max(ans,n-sum[arr[5]]-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]-sum[arr[4]]-sum[arr[5]]);
ans=max(ans,n-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]-sum[arr[3]]);
ans=max(ans,n-sum[arr[4]]-sum[arr[5]]);
ans=max(ans,n-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]-sum[arr[4]]);
ans=max(ans,n-sum[arr[5]]-sum[arr[6]]);
ans=max(ans,n-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]-sum[arr[3]]);
ans=max(ans,n-sum[arr[4]]-sum[arr[5]]);
ans=max(ans,n-sum[arr[6]]-sum[arr[7]]);
ans=max(ans,n-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
}while(next_permutation(arr,arr+10));
printf("%d\n",res);
}
return 0;
}
Solo
傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=6781
題意
-.- 太長就不贅述了。
分析
一開始以為是貪心,能比bob快就做,不能就跳。結果連樣例都過不了。。
這種題目貪心不行自然就想DP了。
定義 \(DP[i][j]\) ,表示在前 \(i\) 題中做出來 \(j\) 題用的時間。初始化為\(10^{18}\)
因為bob是順序做題的,而題目給出的條件可以保證bob每一題都做,且每一題的時間都花完,那麼我們可以用 \(sum[i]\) 表示bob做完前 \(i\) 題用的時間。用 \(arr[i]\) 表示alice單獨做第 \(i\) 題的時間。
那麼轉移方程就是
\[if(i>j)dp[i][j]=min(dp[i][j],dp[i-1][j]);\\if(arr[i]+dp[i-1][j-1]<=sum[i])dp[i][j]=min(dp[i][j],arr[i]+dp[i-1][j-1]); \]
如果嘗試的題目大於做出來的題目時,我們可以從前一題同樣做出來 \(j\) 題的情況轉移過來。
如果當前題目能做出來,則判斷做這道題能否更快。
最後看在嘗試 \(n\) 題的情況下,\(dp[n][i]!=10^{18}\) 當做 \(i\) 的最大值就是答案。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e3+10;
long long arr[MAXN],brr[MAXN],sum[MAXN];
long long dp[MAXN][MAXN];
const long long INF=1e18;
int n;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",arr+i);
for(int i=1;i<=n;i++)scanf("%lld",brr+i),sum[i]=sum[i-1]+brr[i];
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)dp[i][j]=INF;
for(int i=0;i<=n;i++)dp[i][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
if(i>j)dp[i][j]=min(dp[i][j],dp[i-1][j]);
if(arr[i]+dp[i-1][j-1]<=sum[i])dp[i][j]=min(dp[i][j],arr[i]+dp[i-1][j-1]);
}
}
for(int i=n;i>=0;i--){
if(dp[n][i]!=INF){
printf("%d\n",i);
break;
}
}
}
return 0;
}