b_pat_排序题目大杂烩(map+排序+模拟)

最佳排名

对于每个学生,输出他的最佳排名以及该排名对应的是哪项成绩。
当多项排名相同,且都为最佳时,按照 A>C>M>E 的优先级,选择输出哪项成绩。
如果无法查询到该学生的成绩,则输出 N/A。

每门课也有很多下属(每个学生该门课的成绩);每个学生也有很多下属(每个学生的所有成绩);故需要两个结构

#include<bits/stdc++.h>
using namespace std;
const int N=2005;
unordered_map<string, vector<int>> mp;
vector<int> scores[4];

int get_rk(int courseID, int score) {
    int l=0, r=scores[courseID].size();
    while (l<r) {
        int m=l+(r-l)/2;
        if (scores[courseID][m]>score) l=m+1;
        else r=m;
    }
    return l+1;
}
int main() {
    int n,m,info[4]; scanf("%d %d", &n, &m);
    string id;
    for (int i=0; i<n; i++) {
        cin>>id, memset(info, 0, sizeof info);
        for (int j=1; j<4; j++) scanf("%d", &info[j]), info[0]+=info[j];
        info[0]=round(info[0]/3.0);
        for (int j=0; j<4; j++) {
            mp[id].push_back(info[j]);   //id同学的成绩
            scores[j].push_back(info[j]);//第j门课的所有乘积
        }
    }
    for (int i=0; i<4; i++) sort(scores[i].begin(), scores[i].end(), [&](int a, int b){return a>b;});

    string sID;
    char course_name[4]={'A', 'C', 'M', 'E'};
    for (int i=0; i<m; i++) {
        cin>>sID;
        if (mp.find(sID)==mp.end()) printf("N/A\n");
        else {
            //得到sID的排名
            int rk=n+1;
            char course;
            for (int j=0; j<4; j++) {
                int ans_rk=get_rk(j, mp[sID][j]);
                if (ans_rk<rk) {
                    rk=ans_rk;
                    course=course_name[j];
                }
            }
            printf("%d %c\n", rk, course);
        }
    }
    return 0;
}

PAT 排名

然后用以下格式输出最终成绩排名列表:
registration_number final_rank location_number local_rank
具有相同分数的人的排名也要相同,相同分数的人,考号较小的先输出。

先更新局部排名,再将更新好的学生node放到all数组中,然后更新all中的学生排名,最后输出信息

#include<bits/stdc++.h>
using namespace std;
const int N=105;
struct node {
    string sID;
    int score, localID, final_rk, local_rk;
};
int n;
vector<node> locations[N];
vector<node> all;
bool cmp(node& a, node& b) {
    if (a.score!=b.score) return a.score>b.score;
    return a.sID<b.sID;
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int tot=0; cin>>n;

    for (int i=0; i<n; i++) {
        int k, score; cin>>k;
        string sID; 
        tot+=k;
        for (int j=0; j<k; j++) {
            cin>>sID>>score;
            locations[i].push_back({sID, score, i});
        }
        sort(locations[i].begin(), locations[i].end(), cmp);
        //更新局部排名
        for (int j=0; j<k; j++) {
            if (j==0 || locations[i][j].score!=locations[i][j-1].score) {
                locations[i][j].local_rk=j+1;
            } else {
                locations[i][j].local_rk=locations[i][j-1].local_rk;
            }
            all.push_back(locations[i][j]);
        }
    }
    cout<<tot<<'\n';
    sort(all.begin(), all.end(), cmp);
    for (int i=0; i<all.size(); i++) {
        if (i==0 || all[i].score!=all[i-1].score) 
            all[i].final_rk=i+1;
        else 
            all[i].final_rk=all[i-1].final_rk;
        cout<<all[i].sID<<' '<<all[i].final_rk<<' '<<all[i].localID+1<<' '<<all[i].local_rk<<'\n';
    }
    return 0;
}

pat评测

以下列格式输出排名列表:
rank user_id total_score s[1] ... s[K]
其中,rank 是根据 total_score 计算的,所以拥有相同 total_score 的用户的 rank 也相同。
s[i] 是第 i 个问题获得的分数。

这种题一定要理清头绪,不能急啊,

#include<bits/stdc++.h>
using namespace std;
unordered_map<string, vector<int>> mp;
const int unsubmit=-2;
struct node {
    string uID;
    int tot_score, ac_cnt;
    vector<int> k_score;
    int rk;
};
bool cmp(node& a, node& b) {
    if (a.tot_score!=b.tot_score) return a.tot_score>b.tot_score;
    if (a.ac_cnt!=b.ac_cnt) return a.ac_cnt>b.ac_cnt;
    return a.uID<b.uID; 
}
int main() {
    int n,k,m; scanf("%d%d%d", &n,&k,&m);    //总用户数,问题数,提交总数
    int p[k]; for (int i=0; i<k; i++) scanf("%d", &p[i]);
    char buff[15];
    int pID, score;
    for (int i=0; i<m; i++) {
        scanf("%s%d%d", buff, &pID, &score); string uID(buff);
        pID-=1;
        if (mp.find(uID)==mp.end()) mp.insert({uID, vector<int>(5+1, unsubmit)});
        mp[uID][pID]=max(mp[uID][pID], score);
    }
    vector<node> v;
    for (auto& info : mp) {
        string uID=info.first;
        auto& scores=info.second;
        int ac_cnt=0, tot_score=0, is_never_submit=true;
        for (int i=0; i<scores.size(); i++) {
            int s=scores[i];
            if (s!=-1 && s!=unsubmit) tot_score+=s,  is_never_submit=false;
            if (s==p[i]) ac_cnt++;
        }
        if (!is_never_submit) v.push_back({uID, tot_score, ac_cnt, scores});
    }
    sort(v.begin(), v.end(), cmp);
    for (int i=0; i<v.size(); i++) {
        if (i==0 || v[i].tot_score!=v[i-1].tot_score) {
            v[i].rk=i+1;
        } else {
            v[i].rk=v[i-1].rk;
        }
    }
    for (auto& a : v) {
        printf("%d %s %d", a.rk, a.uID.c_str(), a.tot_score);
        for (int j=0; j<k; j++) {
            if (a.k_score[j]==unsubmit) printf(" -");
            else if (a.k_score[j]==-1) printf(" 0");
            else printf(" %d", a.k_score[j]); 
        }
        printf("\n");
    }
    return 0;
}

校园内的汽车

现在,给定你所有的有用信息,你需要做的是:

  • 对于一些查询,计算出查询时刻校园内的汽车数量。
  • 在一天结束时,找到在校园内停放时间最长的汽车

电话账单

对于每个客户,首先以示例显示的格式在一行中打印客户名称和帐单月份。
然后,对于每个通话时间段,在一行中分别打印开始和结束时间和日期(dd:hh:mm),持续时间(以分钟为单位)和通话费用。
通话必须按时间顺序列出。

由于月份是相同的,故以输入月份的1号为基准值,将通过将输入的日期-基准值转化为分钟方便计算;

#include<bits/stdc++.h>
using namespace std;
const int N=1005, ON=1, OFF=0;
int cost[25];

struct node {
    string name;
    int month,day,hour,minute,state,use_time;
} A[N];

double convert(node& info) {
    double ans=cost[info.hour]*info.minute+cost[24]*info.day*60;
    for (int i=0; i<info.hour; i++) ans+=cost[i]*60;
    return ans/100.0;
}

int main() {
    for (int i=0; i<24; i++) scanf("%d", &cost[i]), cost[24]+=cost[i];
    int n; scanf("%d", &n);

    for (int i=0; i<n; i++) {
        cin>>A[i].name; 
        scanf("%d:%d:%d:%d", &A[i].month, &A[i].day, &A[i].hour, &A[i].minute);
        A[i].use_time=A[i].day*24*60+A[i].hour*60+A[i].minute;
        string state_string; cin>>state_string;
        A[i].state=(state_string=="on-line" ? ON : OFF);
    }
    sort(A, A+n, [&](node a, node b) {
        return (a.name!=b.name) ? a.name<b.name : a.use_time<b.use_time;
    });
    map<string, vector<node>> mp;
    for (int i=1; i<n; i++) if (A[i].name==A[i-1].name && A[i-1].state==ON && A[i].state==OFF) {
        mp[A[i].name].push_back(A[i-1]);
        mp[A[i].name].push_back(A[i]);
    }

    for (auto& C : mp) {    //主人
        auto& records=C.second;
        double tot=0;
        printf("%s %02d\n", C.first.c_str(), records[0].month);
        for (int i=1; i<records.size(); i+=2) {
            auto start=records[i-1]; 
            auto& end=records[i];
            double d=convert(end)-convert(start);
            printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n", start.day, start.hour, start.minute, end.day, end.hour, end.minute, end.use_time-start.use_time, d);
            tot+=d;
        }
        printf("Total amount: $%.2f\n", tot);
    }
    return 0;
}

研究生入学

每个申请人都必须提供两个成绩:全国入学考试成绩 GE 和面试成绩 GI,申请人的最终成绩是 (GE+GI)/2。
录取规则如下:

  • 申请者将根据其最终成绩由高到低进行排名,并且将从排名列表的顶部开始逐一录取。
  • 如果申请者的最终成绩并列,则按照 GE 成绩由高到低进行排名,如果成绩仍然并列,则并列者的排名必须相同。
  • 每个申请人可以填报 K 个志愿,并且将根据他/她的志愿进行录取:如果按照排名列表,轮到某位学生被录取了,并且其第一志愿学校还未招满人,则他成功被该学校录取。如果名额已满,则按顺序考虑其他志愿,直至成功被录取为止。如果所有报名学校都无法录取该名学生,则该名学生录取失败。
  • 如果出现并列排名,并且并列申请人正在申请同一所学校,那么该学校不得只录取其中一部分申请人,即使超过招生限额,也必须全部录取。

每所学校的录取结果占据一行,其中包含所有学校录取的申请人的编号。编号必须按升序排列,并用空格分隔。
每行的末尾必须没有多余的空格。如果某学校没有录取任何学生,则必须相应地输出空白行。

耐心...

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
struct node {
    int sID, Ge, Gi, Gf;
    int choise[6];
} S[N];
bool cmp1(node& a, node& b) {
    if (a.Gf!=b.Gf) return a.Gf>b.Gf;
    if (a.Ge!=b.Ge) return a.Ge>b.Ge;
    return a.sID<b.sID;
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n,m,k; cin>>n>>m>>k; //n是应用的总数,m是毕业院校总数,k学生的申请总数
    int limit[m];
    //第i个院校的限额
    for (int i=0; i<m; i++) cin>>limit[i];
    for (int i=0; i<n; i++) {
        cin>>S[i].Ge>>S[i].Gi;
        S[i].sID=i, S[i].Gf=S[i].Ge+S[i].Gi;
        for (int j=0; j<k; j++) {
            cin>>S[i].choise[j];
        }
    }
    sort(S, S+n, cmp1);
    //输出每个院校录取的学生编号
    vector<node> school[m];
    for (int i=0; i<n; i++) 
    for (int j=0; j<k; j++) {
        int schID=S[i].choise[j];
        if (limit[schID]>school[schID].size() || (school[schID].back().Ge==S[i].Ge && school[schID].back().Gf==S[i].Gf)) {
            school[schID].push_back(S[i]);
            break;  //如果考前的志愿被录取,则不考虑后面的志愿
        }
    }
    for (int i=0; i<m; i++) {
        sort(school[i].begin(), school[i].end(), [&](node a, node b) {return a.sID<b.sID;});
        for (int j=0; j<school[i].size(); j++) {
            auto& stu=school[i][j];
            if (j==school[i].size()-1) cout<<stu.sID;
            else cout << stu.sID << ' ';
        }
        cout<<'\n';
    }
    return 0;
}

校原内的汽车

现在,给定你所有的有用信息,你需要做的是:

  • 对于一些查询,计算出查询时刻校园内的汽车数量。
  • 在一天结束时,找到在校园内停放时间最长的汽车。

比较繁琐的一道:秒转化为时间 hh:mm:ss

  • hh=time/3600
    mm=time%3600/60比如1800对应1800/60=30(分针在30分钟的位置)
    ss=time%60
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5, K=8e4+5;
const char IN='i', OUT='o';
struct node {
    string num;
    int time, isParking;
    char move;  //动作in/out
} C[N];
bool cmp(node& a, node& b) {
    if (a.num==b.num) return a.time<b.time; //优先考虑时间靠前的记录
    return a.num<b.num;
}
bool cmp1(node& a, node& b) {
    if (a.isParking==b.isParking) {
        if (a.time==b.time) return a.num<b.num;
        return a.time<b.time;
    }
    return a.isParking>b.isParking; //1>0,优先选出在
}
map<string, int> mp;
int main() {
    int n,k; scanf("%d%d", &n,&k);
    char t[10], move[5];
    int hour,minute,second; 
    for (int i=0; i<n; i++) {
        scanf("%s %d:%d:%d %s", t,&hour,&minute,&second,move);
        C[i].num=string(t), C[i].time=hour*3600+minute*60+second, C[i].move=move[0];
    }
    sort(C,C+n,cmp);
    //记录每辆车的停车时间
    int max_parkingtime=0;
    for (int i=1; i<n; i++) if (C[i].num==C[i-1].num && C[i-1].move==IN && C[i].move==OUT) {
        mp[C[i].num]+=C[i].time-C[i-1].time;
        C[i].isParking=C[i-1].isParking=true;
        max_parkingtime=max(max_parkingtime, mp[C[i].num]);
    }
    //再排一下序,将不合法的车子弄到数组尾部
    sort(C, C+n, cmp1);
    int j=0, cnt=0;
    while (k--) {
        scanf("%d:%d:%d", &hour,&minute,&second);
        int deadline=hour*3600+minute*60+second;
        while (C[j].isParking && C[j].time<=deadline) {
            if (C[j].move==IN) cnt++;
            else               cnt--;
            j++;
        }
        printf("%d\n", cnt);
    }
    for (auto& info : mp) if (info.second==max_parkingtime) {
        printf("%s ", info.first.c_str());
    }
    printf("%02d:%02d:%02d", max_parkingtime/3600, max_parkingtime%3600/60, max_parkingtime%60);
    return 0;
}

快速排序的中轴

给定一个序列,找到这个序列中可以作为快排中轴的元素

思路
傻了,一开始直接用单调栈求出右边大于自己的数的个数到 ans 数组中,然后中轴就是左边小于自己的数的个数为 n-ans[i]-1,这是不对的,比如:

[1,3,2,4,5],2并不可以作为中轴,但 5-2-1=2,错误认为2是中轴

冷静了一下,想了个暴力:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n; cin>>n;
    int A[n]; for (int i=0; i<n; i++) cin>>A[i];

    // stack<int> st;
    // int ans[n]; memset(ans, 0, sizeof ans);
    // for (int i=n-1; i>=0; i--) {
    //     while (!st.empty() && A[st.top()]<A[i]) st.pop();
    //     ans[i]=st.empty() ? 0 : n-st.top();
    //     st.push(i);
    // }
    // int cnt=0;
    // for (int i=0; i<n; i++) if (ans[i]==n-i-1) 
    //     cnt++;
    // cout << cnt << '\n';
    // for (int i=0; i<n; i++) if (ans[i]==n-i-1) { 
    //     if (i==n-1) cout << A[i];
    //     else cout << A[i] <<' ';
    // }
    int lmax[n], rmin[n]; 
    memset(lmax, 0, sizeof lmax), memset(rmin, 0x3f3f3f3f, sizeof rmin);
    lmax[0]=A[0], rmin[n-1]=A[n-1];
    for (int i=1; i<n; i++) lmax[i]=max(lmax[i-1], A[i]);
    for (int i=n-2; i>=0; i--) rmin[i]=min(rmin[i+1], A[i]);
    
    vector<int> ans; 
    for (int i=0; i<n; i++) if (A[i]>=lmax[i] && A[i]<=rmin[i]) {
        ans.push_back(A[i]);
    }
    sort(A.begin(), A.end());
    cout << ans.size() << '\n';
    if (ans.size()==0) cout << '\n';
    else for (int i=0; i<ans.size(); i++) {
        if (i==ans.size()-1) cout<<ans[i];
        else        cout<<ans[i]<<' '; 
    }
    return 0;
}

合影

每行的人数必须为 N/K(向下取整),所有多余的人(如果有)都放到最后一行;
位于后排的所有人都不得矮于位于前排的任何人;
在每一行中,最高的人站在该行的中心位置(定义为位置 (m/2+1),位置从1开始编号,其中 m 是该行的总人数,除法结果向下取整);
在每一行中,其他人必须按照其身高非递增顺序依次排入该行,交替地将他们的位置先安排到最高人的右侧,然后再安排到最高人的左侧
身高相同时,必须按姓名的字典序升序进行排序

思路
这题如果真的是从中心开始扩张,每次扩张都需要用O(logn)时间(最优了吧)去找最高和次高进行安排,然后删除他两,比较慢;比较聪明的做法是:事先对学生数组 A 按照规则排序,对于每一行,从中心开始扩展,我都用一个数组记录扩张时的人的在 A 中的索引

#include<bits/stdc++.h>
using namespace std;
struct node {
    string name;
    int h;
};
bool cmp(node& a, node& b) {
    if (a.h!=b.h) return a.h>b.h;
    return a.name<b.name;
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n,k; cin>>n>>k;
    node A[n+1];
    for (int i=0; i<n; i++) cin>>A[i].name>>A[i].h;
    sort(A, A+n, cmp);
    int idx=0;
    
    for (int i=0; i<k; i++) {
        int sz=round(n/k);
        if (i==0) sz+=n%k;
        int row[sz+1], m=sz/2+1, l=m-1, r=m;
        while (l>0 || r<=sz) {
            if (r<=sz) row[r++]=idx++;
            if (l>0) row[l--]=idx++;
        }
        for (int j=1; j<=sz; j++) {
            cout<<A[row[j]].name;
            if (j!=sz) cout<<' ';
        }
        cout<<'\n';
    }
    return 0;
}

世界首富

接下来输出给定年龄段内最富有的 M 个人的信息,格式为:
Name Age Net_Worth
输出必须按照净资产的非递增顺序排列,如果净资产相同,则按照年龄不降序排列,如果年龄也相同,则按照字典序不降序排列。

思路
如果数据不卡暴力算法还好,就要优化:题目限定每个case最多输出100个人,所以可用一个数组记录每个年龄出现的次数,某个年龄出现次数多余100时,后面的人不用管了,反正都输出不了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
struct node {
    string name;
    int age, nw;
};
bool cmp(node& a, node& b) {
    if (a.nw!=b.nw) return a.nw>b.nw;
    if (a.age!=b.age) return a.age<b.age;
    return a.name<b.name;
}
int main() {
    int n,k; scanf("%d%d", &n, &k);
    node A[n];
    string name; 
    int age, nw;
    for (int i=0; i<n; i++) {
        cin>>A[i].name;
        scanf("%d%d", &A[i].age,&A[i].nw);
    }
    sort(A, A+n, cmp);
    int m, lo, hi;
    for (int i=0; i<k; i++) {
        scanf("%d%d%d", &m,&lo,&hi);
        printf("Case #%d:\n", i+1);
        vector<node> candidates;
        int cnt=0;
        while (cnt<m) {
            int i;
            for (i=0; i<n && cnt<m; i++) if (A[i].age>=lo && A[i].age<=hi) {
                candidates.push_back(A[i]);
                cnt++;
            }
            if (i==n) break;
        }
        sort(candidates.begin(), candidates.end(), cmp);
        if (candidates.empty()) printf("None\n");
        else for (auto& c : candidates) {
            printf("%s %d %d\n", c.name.c_str(), c.age, c.nw);
        }
    }
    return 0;
}
posted @ 2020-09-17 21:36  童年の波鞋  阅读(190)  评论(0编辑  收藏  举报