1. 程式人生 > 實用技巧 >2020 年百度之星程式設計大賽初賽二部分題解

2020 年百度之星程式設計大賽初賽二部分題解

弱雞隻做出來五題。。

Poker

傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=6775

題意

小沃沃在玩一個有趣的遊戲。

初始他有 \(n\) 塊錢,每一輪他需要投入至少 \(m\) 塊錢,系統會拿走其中 \(p\%\) 的錢,並把剩下的錢還給他。

請問在最優情況下,小沃沃最多可以玩多少輪?

假設當前一輪小沃沃投入了 \(x\) 塊錢,那麼他可以收回$ ⌊x\times (100−p)/100 ⌋ $塊錢,其中 \(⌊a⌋\) 表示 \(a\) 取下整。

小沃沃每一輪投入的錢不能超過他現在擁有的錢。

每一輪投入的錢必須為整數。

分析

那就是每一次就丟 \(m\)

塊錢進去,除了最後一次之外,每一次丟的實際錢數其實是 \(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;
}