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;
}