本章题解跳转 | 考点 |
---|---|
P1001 | 数字的数组表示和处理 |
P1002 | 多项式的数组表示和处理 |
P1003 | 深度优先搜素 |
P1004 | 深度优先搜素 |
P1005 | 哈希表 |
P1006 | |
P1007 | 数组子区间求和,动态规划 |
P1008 | |
P1009 | 多项式的数组表示和处理 |
P1010 | 转换进制,二分法 |
P1011 | |
P1012 | 排序 |
P1013 | 深度优先搜素 |
P1014 | 排队问题 |
P1015 | 转换进制 |
P1016 | 哈希表,排序 |
P1017 | 排队问题 |
P1018 | 深度优先搜素 |
P1019 | 转换进制 |
P1020 | 树的构造 |
P1021 | 深度优先搜素 |
P1022 | 哈希表 |
P1023 | 数字的数组表示和处理,哈希表 |
P1024 | 数字的数组表示和处理 |
P1025 | 排序 |
P1026 | 数字的数组表示和处理,排队问题 |
P1027 | 转换进制 |
P1028 | 排序 |
P1029 | 排序 |
P1030 | 深度优先搜素 |
P1031 | |
P1032 | 静态链表 |
P1034 | 深度优先搜素 |
P1035 | |
P1036 | |
P1037 | |
P1038 | |
P1039 | 哈希表 |
P1040 | 对称字符串 |
P1041 | 哈希表 |
P1042 | |
P1043 | 树的构造 |
P1044 | 双指针 |
P1045 | 动态规划 |
P1046 | 数组子区间求和 |
P1047 | 排序 |
P1048 | 双指针 |
P1049 | 分支法 |
P1050 | 哈希表 |
P1051 | 栈 |
P1052 | 静态链表,排序 |
P1053 | 深度优先搜素 |
P1054 | 哈希表 |
P1055 | 排序 |
P1056 | |
P1057 | |
P1058 | |
P1059 | |
P1060 | |
P1061 | |
P1062 | 排序 |
P1063 | 哈希表 |
P1064 | 树的构造 |
P1065 | |
P1066 | |
P1067 | |
P1068 | |
P1069 | |
P1070 |
P1001
题目链接:题目详情 - 1001 A+B Format (pintia.cn)
题目大意:将两个数A和B相减,每隔3个数输出一个空格(注意,从后往前数)
思路:如果是负数先把负号去掉,用一个字符串存入答案,从后往前,数到3个数存入一个逗号,并且吧cnt更新为0,用reverse函数倒过来输出
实现代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int a,b;//输入两个数
cin>>a>>b;
int sum=a+b;//求和
if(sum<0){
cout<<'-';
sum=-sum;
}//去掉负号
string s=to_string(sum);//转换成字符串
int cnt=0,len=s.length();
string ans="";//存答案字符串
for(int i=len-1;i>=0;i--){
if(cnt==3){
ans+=',';
cnt=0;
}//数到3加个逗号
ans+=s[i];
cnt++;
}
reverse(ans.begin(),ans.end());//翻转
cout<<ans;
return 0;
}
P1002
题目链接:题目详情 - 1002 A+B for Polynomials (pintia.cn)
题目大意:输入两行,每行第一个为k,当前行多项式的数量,随后给出k组,N为指数,ai为项数
将所有指数相同的相加从后往前输出即可
思路:先遍历一遍看指数个数,输出,随后再遍历一遍,如果当前数组值不为0说明存在当前项,输出当前项和项数,注意末尾不能有多余空格
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
double exponents[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
for(int i=0;i<2;i++){
int k;
cin>>k;
while(k--){
int n;
double m;
cin>>n>>m;
exponents[n]+=m;
}
}
int cnt=0;
for(int i=0;i<N;i++)
if(exponents[i])
cnt++;
cout<<cnt;
for(int i=N-1;i>=0;i--){
if(exponents[i]){
printf(" %d %.1lf",i,exponents[i]);
}
}
return 0;
}
P1003
题目链接:题目详情 - 1003 Emergency (pintia.cn)
题目大意:第一行输入n个点m条边,起点终点,随后给定m行,代表两个点之间的距离,随后一行输入n个数,代表第i个点的救援队数量,每到一个点就可以获得这个点的救援队,要求输出从起点到终点的最短路径数量和求得的最短路径中救援队数量最大值
思路:这里用vector存点与点之间的关系,用g数组存两点之间的距离,未避免自环和重复边,要先将g数组设为0x3f3f3f3f,用一个dist数组储存该点到起始点的最小距离
随用用dfs遍历,如果从起点到当前点的距离大于了之前数组中的设的,说明当前路径不是最短路,要及时止损,再判断是否是终点的情况,如果是终点,判段当前距离是不是和数组中存的一样,如果一样说明也是最短路,更新救援队数量和路径数,如果不一样就说明有更短的距离,重置救援队数量和路径数,再将当前点更新最短路
如果不是最终点,循环当前点的每个边,往下dfs
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int rescue[N];//救援队数量
vector<int> v[N];//边之间的关系
int g[N][N];//存边之间的大小
int dist[N];//最短路径
int n,m,c1,c2;//城市数量,边的数量,c1,c2;
int paths,teams;
void dfs(int now,int team_num,int distance){//当前点,救援队数量,原点到当前点的距离
if(distance>dist[now]) return;//当前点距离更大
if(now==c2){//更新最短路
if(distance==dist[c2]){
paths++;
teams=max(teams,team_num);
}else{
dist[now]=distance;
paths=1;
teams=team_num;
}
}else{//往下遍历
dist[now]=distance;
for(int i=0;i<v[now].size();i++){
int j=v[now][i];
dfs(j,team_num+rescue[j],distance+g[now][j]);
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>c1>>c2;
for(int i=0;i<n;i++) cin>>rescue[i];
memset(g,0x3f,sizeof g);
memset(dist,0x3f,sizeof dist);//初始化
while(m--){
int a,b,c;
cin>>a>>b>>c;
v[a].push_back(b);
v[b].push_back(a);
g[a][b]=g[b][a]=min(g[a][b],c);//存边
}
dfs(c1,rescue[c1],0);
cout<<paths<<" "<<teams;
return 0;
}
P1004
题目链接:题目详情 - 1004 Counting Leaves (pintia.cn)
题目大意:给定n个结点,m条非叶结点数量,随后m行输入当前结点,当前结点的子节点个数,子节点id,要求求出每一层的叶子结点数量
思路:用vector来储存每个结点的子结点,dfs遍历树,用一个max_level维护树的最大深度,level_num数组储存当前深度的叶子结点个数。在遍历过程中,如果遇到当前结点深度大于max_level,更新,如果当前结点的子节点个数非空,往下遍历,否则为叶子结点,更新数组
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=110;
vector<int> child[N];
int n,m;//点的数量,非叶子结点数量
int max_level,level_num[N];
void dfs(int id,int level){
if(level>max_level) max_level=level;//更新最大深度
if(child[id].size()){
for(auto t: child[id]){
dfs(t,level+1);
}//往下遍历子节点
}else{
level_num[level]++;//更新深度数组叶子结点个数
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
while(m--){
int id;
int k;
cin>>id>>k;
while(k--){
int x;
cin>>x;
child[id].push_back(x);
}
}//输入处理
dfs(1,1);//从根节点进入
for(int i=1;i<=max_level;i++){
cout<<level_num[i];
if(i!=max_level) cout<<" ";
}//输出
return 0;
}
P1005
题目链接:题目详情 - 1005 Spell It Right (pintia.cn)
题目大意:给定一个最大长度为10的100次方的字符串,求各位相加之后用英文表示
思路:开个long long 类型储存和,再用一个数组表示就好了
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
string arr[10]={"zero","one","two","three","four","five","six","seven","eight","nine"};
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
LL ans=0;
string s;
cin>>s;//输入
int len=s.length();
for(int i=0;i<len;i++){
ans+=s[i]-'0';
}//求和
s=to_string(ans);//转换成字符串
len=s.length();
for(int i=0;i<len;i++){//改成用英文表示
if(i!=0) cout<<" ";
cout<<arr[s[i]-'0'];
}
return 0;
}
P1006
题目链接:题目详情 - 1006 Sign In and Sign Out (pintia.cn)
题目大意:给定m个同学,每个同学有自己id和到达时间和离开时间,找出最早到的和最晚走的
思路:直接用字符串比较这个时间就好了,排序一下
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<string,string> PSS;
vector<PSS> v1;//存到达时间
vector<PSS> v2;//存离开时间
bool cmp1(PSS a,PSS b){
if(a.first!=b.first) return a.first<b.first;
else return a.second<b.second;
}//从小到大
bool cmp2(PSS a,PSS b){
if(a.first!=b.first) return a.first>b.first;
else return a.second<b.second;
}//从大到校
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
while(n--){
string id,start,end;
cin>>id>>start>>end;
v1.push_back({start,id});
v2.push_back({end,id});
}//数据输入
sort(v1.begin(),v1.end(),cmp1);
sort(v2.begin(),v2.end(),cmp2);//两个排序
for(auto t: v1){
cout<<t.second<<" ";
break;
}
for(auto t: v2){
cout<<t.second;
break;
}
//都输出第一个
return 0;
}
P1007
题目链接:题目详情 - 1007 Maximum Subsequence Sum (pintia.cn)
题目大意:给定一个长度k,下一行输入k个数,要求获得当前序列中子序列和最大的一段,输出最大的和开始的起点和终点
思路:存下前缀和数组,遍历一遍序列,考虑以当前结点为终点的最大子序列,最大的子序列必然是前k-1项中sum[i]最小的那个,只要用一个lowest维护最小的下标就可以了,判段以当前结点结尾和以lowest+1为起点,大小是否大于维护的最大值,更新即可
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int a[N],sum[N];//当前数组,前缀和数组
int result=-1;//最大值
int l,r,lowest;//左右下标,最低点
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int k;
cin>>k;
for(int i=1;i<=k;i++) {
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}//输入处理,前缀和处理
for(int i=1;i<=k;i++){
//更新最大值,更新的时候把起点和终点一起更新
if(sum[i]-sum[lowest]>result){
result=sum[i]-sum[lowest];
l=lowest+1;
r=i;
}
//维护i之前的最小前缀和
if(sum[lowest]>sum[i]) lowest=i;
}
//判段是否被更新过,如果没有被更新过则全为负数则需要按照题目输出
if(result==-1) cout<<"0 "<<a[1]<<" "<<a[k];
else{
cout<<result<<" "<<a[l]<<" "<<a[r];
}
return 0;
}
P1008
题目链接:题目详情 - 1008 Elevator (pintia.cn)
题目大意:给定一个序列,第一项为n,代表后面有多少个数,每个数代表电梯要往哪层走,每上走一层时间加6,下走一层时间加4,到达一次目标层时间加5,求最终时间
思路:没有思路,硬做就是了
实现代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int cost=0;
int n,last=0;
cin>>n;
for(int i=1;i<=n;i++){
cost+=5;
int x;
cin>>x;
int dist=x-last;
if(dist>0) cost+=dist*6;
else cost-=dist*4;
last=x;
}
cout<<cost;
return 0;
}
P1009
题目链接:题目详情 - 1009 Product of Polynomials (pintia.cn)
题目大意:给定两个多项式,求两个多项式乘积。输出要求先输出多项式的项数,再将多项式从高次往低次输出,末尾不能有多余空格
思路:用两个数组储存两个数组的指数和项数,后用ans数组储存两个相乘的情况,先遍历一遍得到cnt,再遍历一遍得到多项式
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
double a[N],b[N],ans[N*2];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int k;
cin>>k;
while(k--){
int n;
double ai;
cin>>n>>ai;
a[n]+=ai;
}
cin>>k;
while(k--){
int n;
double bi;
cin>>n>>bi;
b[n]+=bi;
}
//上面是两个多项式的输入
for(int i=0;i<N;i++){
if(a[i]){
for(int j=0;j<N;j++){
if(b[j]){
ans[i+j]+=a[i]*b[j];
}
}
}
}
//多项式相乘,当前系数项数不为0,就把b中项数不为0的全乘一遍加到ans数组
int cnt=0;
for(int i=0;i<N*2;i++){
if(ans[i]) cnt++;
}
//计算项数
cout<<cnt;
for(int i=N*2-1;i>=0;i--){
if(ans[i]){
printf(" %d %.1lf",i,ans[i]);
}
}
//按要求输出
return 0;
}
P1010
题目链接:题目详情 - 1010 Radix (pintia.cn)
题目大意:给定两个数N1,N2,输入N1,N2,tag,radix如果tag等于1,表示N1的进制,如果tag等于2表示N2的进制,求得另一个数为多少进制才能与当前进制相等,如果都不能则输出Impossible
思路:首先是数据的处理,如果tag等于2,就让N1和N2交换就好了,始终表示N1的进制。
然后遍历一遍每个进制,找到进制相等的情况就好了,但是这样的时间复杂度太高了,所以用二分来做,才能拿到满分
考虑两个边界情况,比如7进制下,各项最大为6,所以左边界l是N2所有数中最大的数加1,而右边界则是将N1转换成10进制,因为这样取右边届只要N2大于就就会超,等于1则正好,小于则取不到。
然后就是二分,这里用的找最小数的二分模板,当前数大于,则需要往小取,反之则需要往大取、
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL trans(char c){
if(isdigit(c)) return c-'0';
return c-'a'+10;
}//进制字母转换
LL translate(string N,LL radix){
//将N由radix进制转换成10进制
LL sum=0,unit=1;
for(int i=N.length()-1;i>=0;i--,unit*=radix){
sum+=trans(N[i])*unit;
if(sum<0||unit<0) return -1;//如果小于0说明爆LL了,直接返回-1
}
return sum;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string N1,N2;
LL tag,radix,l=2,r,mid;
cin>>N1>>N2>>tag>>radix;
if(tag==2) swap(N1,N2);
//输入处理
LL now=r=translate(N1,radix);//将右边界设为10进制下的N1转换
for(int i=0;i<N2.length();i++){
l=max(l,trans(N2[i])+1);
}//获得N2中最大数
while(l<=r){//二分获得进制
mid=l+r>>1;
LL t=translate(N2,mid);
if(t>=now||t<0) r=mid-1;
else l=mid+1;
}
//如果当前进制下的l不能获得答案,说明找不到
if(translate(N2,l)==now){
cout<<l;
}else{
cout<<"Impossible";
}
return 0;
}
P1011
题目链接:题目详情 - 1011 World Cup Betting (pintia.cn)
题目大意:就是给三组数据,每组中找到最大的输出该买哪一种,然后按他给的公式求值就可以了
思路:直接做
实现代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
double aa=1;
for(int i=0;i<3;i++){
double W,T,L;
cin>>W>>T>>L;
double mx=max(W,T);
mx=max(mx,L);
aa*=mx;
if(mx==W) cout<<"W ";
else if(mx==T) cout<<"T ";
else cout<<"L ";
}
printf("%.2lf",(aa*0.65-1)*2);
return 0;
}
P1012:
题目链接:题目详情 - 1012 The Best Rank (pintia.cn)
题目大意:给定N个学生,M组询问,随后N行每行代表一个学生id,C,M,E成绩
要求输出询问学生的A,C,M,E排名最前的一门的排名和当前科目,如果有重复就要按照A,C,M,E优先级排序(A为3门平均成绩)
思路:分别按照A,C,M,E获得以A,C,M,E顺序下的各学生各科目排名,然后用哈希表存当前学生在结构体数组中的位置,按照询问,获得当前学生排名最前的科目即可
实现代码:
#include<bits/stdc++.h>
using namespace std;
struct student{
string id;
int score[4],rank[4];//0123分别代表ACME
}stu[2010];
//分别代表4个排序
bool cmp1(student A,student B){
return A.score[0]>B.score[0];
}
bool cmp2(student A,student B){
return A.score[1]>B.score[1];
}
bool cmp3(student A,student B){
return A.score[2]>B.score[2];
}
bool cmp4(student A,student B){
return A.score[3]>B.score[3];
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++){
string name;
cin>>name;
int c,m,e;
cin>>c>>m>>e;
stu[i].id=name;
stu[i].score[0]=(c+m+e)/3;
stu[i].score[1]=c;
stu[i].score[2]=m;
stu[i].score[3]=e;
}
//输入处理
sort(stu,stu+n,cmp1);
for(int i=0;i<n;i++){
if(i==0||stu[i].score[0]!=stu[i-1].score[0]) stu[i].rank[0]=i+1;
else stu[i].rank[0]=stu[i-1].rank[0];
}
sort(stu,stu+n,cmp2);
for(int i=0;i<n;i++){
if(i==0||stu[i].score[1]!=stu[i-1].score[1]) stu[i].rank[1]=i+1;
else stu[i].rank[1]=stu[i-1].rank[1];
}
sort(stu,stu+n,cmp3);
for(int i=0;i<n;i++){
if(i==0||stu[i].score[2]!=stu[i-1].score[2]) stu[i].rank[2]=i+1;
else stu[i].rank[2]=stu[i-1].rank[2];
}
sort(stu,stu+n,cmp4);
for(int i=0;i<n;i++){
if(i==0||stu[i].score[3]!=stu[i-1].score[3]) stu[i].rank[3]=i+1;
else stu[i].rank[3]=stu[i-1].rank[3];
}
//4种排序,注意分数与前一个相同就把前面排名赋给当前排名
unordered_map<string,int> mp;
for(int i=0;i<n;i++) mp[stu[i].id]=i;
while(m--){
string name;
cin>>name;
if(!mp.count(name)){
cout<<"N/A"<<endl;
//如果哈希表中没有这个名字
}else{
//找到排名最前的科目
int t=mp[name];
int mi=2010;
for(int i=0;i<4;i++){
mi=min(mi,stu[t].rank[i]);
}
if(mi==stu[t].rank[0]){
cout<<mi<<" A"<<endl;
}else if(mi==stu[t].rank[1]){
cout<<mi<<" C"<<endl;
}else if(mi==stu[t].rank[2]){
cout<<mi<<" M"<<endl;
}else if(mi==stu[t].rank[3]){
cout<<mi<<" E"<<endl;
}
}
}
return 0;
}
P1013
题目链接:题目详情 - 1013 Battle Over Cities (pintia.cn)
题目大意:给定n个点,m条边和k个询问,随后m行输入m条边的关系,随后一行输出k个数代表询问,询问意思为一个图中删除当前点,还需要建立多少条边保持连通
思路:只需要找删除当前点图被分成了几份,减去一个1就可以了,用dfs遍历一遍,每需要遍历一次,份数加1
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,k;//n个点,m个城市,k个询问
int lost;//丢失的城市
vector<int> v[1010];// 存图之间的关系
bool st[1010];//是否被遍历
void dfs(int x){
st[x]=true;//将当前结点改为遍历过了
for(int i=0;i<v[x].size();i++){
int j=v[x][i];
if(j!=lost&&!st[j]) dfs(j);
}//遍历当前点连接的其他点中,不是失去的城市的点或者被访问过的点
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>k;
while(m--){
int x,y;
cin>>x>>y;
v[x].push_back(y);
v[y].push_back(x);
}//建图
while(k--){
cin>>lost;
memset(st,0,sizeof st);//初始化
int cnt=0;
for(int i=1;i<=n;i++){
//每次遍历加1
if(!st[i]&&i!=lost){
cnt++;
dfs(i);
}
}
cout<<cnt-1<<endl;
}
return 0;
}
P1014
题目链接:题目详情 - 1014 Waiting in Line (pintia.cn)
题目大意:给定n个窗口,每个窗口前m个队伍,k个总人数,和q个询问。要求获得询问人的离开时间,窗口从8点营业,超过17点输出Sorry
思路:用一个队列数组维护每个窗口前的信息,循环一遍8点到17点这个时间段,首先判段是否有出队的,即离开时间等于当前时间的,再先列后行判段将黄线外的人入队,最后更新窗口前没有更新过的人的时间
实现代码:
#include<bits/stdc++.h>
using namespace std;
int N,M,K,Q;//窗口数,每个窗口人数,总人数,查询数
struct customer{
int process_time,leave_time=0;
}c[1010];
int main(){
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>N>>M>>K>>Q;
for(int i=0;i<K;i++) cin>>c[i].process_time;
queue<int> q[1010];//每个窗口的接待队列
int cur=0;//黄线外的第一个人编号
for(int Time=480;Time<1020;Time++){
//送客
for(int i=0;i<N;i++){
//队前有人且队前人的出队时间等于当前时间
if(q[i].size()&&c[q[i].front()].leave_time==Time){
q[i].pop();
}
}
//入队
for(int j=0;j<M;j++){
for(int i=0;i<N;i++){
//i队当前的序号等于j,因为从0开始遍历,所以等于的时候就有多一个位置
if(q[i].size()<=j&&cur<K){
q[i].push(cur++);
}
}
}
//迎客
for(int i=0;i<N;i++){
//更新最前面的人的时间
if(q[i].size()&&c[q[i].front()].leave_time==0){
c[q[i].front()].leave_time=Time+c[q[i].front()].process_time;
}
}
}
while(Q--){
int x;
cin>>x;
if(c[--x].leave_time==0){
cout<<"Sorry"<<endl;
}else{
printf("%02d:%02d\n",c[x].leave_time/60,c[x].leave_time%60);
}
}
return 0;
}
P1015
题目链接:题目详情 - 1015 Reversible Primes (pintia.cn)
题目大意:给出一个N和一个D,如果N和N在D进制下的反过来的数都为质数,输出Yes,否则输出No
思路:进制转换类题目,写一个translate就好了,将10进制转换成D进制,转过去之后就是反的,所以不需要在转一遍了
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//进制转换
int trans(string num,int radix){
int sum=0;
for(int i=num.length()-1,unix=1;i>=0;i--,unix*=radix){
sum+=(num[i]-'0')*unix;
}
return sum;
}
//质数判段
bool isPrime(int x){
if(x<2) return false;
for(int i=2;i<=x/i;i++){
if(x%i==0) return false;
}
return true;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int x,y;
while(1){
cin>>x>>y;
if(x<0) break;
if(!isPrime(x)){
cout<<"No"<<endl;
}else{
string s="";
while(x){
s+=to_string(x%y);
x/=y;
}
if(isPrime(trans(s,y))){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
}
}
return 0;
}
P1016
题目链接:题目详情 - 1016 Phone Bills (pintia.cn)
题目大意:给定0到23为起点的每个小时中打点话的价格,虽有给出N条打电话信息,要求按照名字字母序输出每个人每次打电话的起始时间和终止时间,以及打了多少分钟和花费的价格,最后输出他一共花费了多少钱
思路:用结构体存每条订单的信息,用哈希表存人名和信息的关系,(这里刚好用map数组将人名顺序排列),随后排序他的所有订单,匹配有效订单信息输出,注意如果没有有效订单不需要输出,所有这里要在第一次匹配的时候才输出
实现代码:
#include<bits/stdc++.h>
using namespace std;
struct record{
int dd,hh,mm,t;
string tag;
};//记录订单信息
double danjia[24];//记录每小时单价
map<string,vector<record>> mp;//哈希表存顾客订单
int N;
bool cmp(record A,record B){
return A.t<B.t;
}//重写排序方式
int main(){
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
for(int i=0;i<24;i++) cin>>danjia[i];
cin>>N;
int month;
for(int i=0;i<N;i++){
string name,tag;
char c;
int date,hour,minute;
cin>>name>>month>>c>>date>>c>>hour>>c>>minute>>tag;
record temp;
temp.dd=date;
temp.hh=hour;
temp.mm=minute;
temp.t=date*1440+hour*60+minute;//按照分钟储存方便计算
temp.tag=tag;
mp[name].push_back(temp);
} //建立人名关系
for(auto t: mp){
auto v=t.second;//获得当前顾客的数组
sort(v.begin(),v.end(),cmp);//按照时间书序排序
double total=0;//总花费
for(int i=0;i<v.size();){
if(i+1<v.size()&&v[i].tag>v[i+1].tag){
//如果这一条和后面一条匹配上了
if(!total){
//第一次匹配输出顾客名字和当前月份
cout<<t.first;
printf(" %02d\n",month);
}
int t1=v[i].t;
int t2=v[i+1].t;
double fenzhang=0;//每一个通话记录的账单
for(int time=t1;time<t2;time++){
fenzhang+=danjia[time%1440/60];
}//整除1440把日除掉,除60获得当前是第几个小时(超过1440分钟的会加一天了)
printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2lf\n",v[i].dd,v[i].hh,v[i].mm,v[i+1].dd,v[i+1].hh,v[i+1].mm,t2-t1,fenzhang/100);
i+=2;//匹配到了就往后走两个
total+=fenzhang;
}else{
i++;//没匹配到就往后走一个
}
}
if(total) printf("Total amount: $%.2f\n",total/100);
}
return 0;
}
P1017
题目链接:题目详情 - 1017 Queueing at Bank (pintia.cn)
题目大意:一个银行前有k个窗口,给定n个用户,给出他们的到达时间和办事时间,到达时间格式为hh:mm:ss, 办事时间以分钟为单位。如果到达时间超过17时则不记录,如果达到时间没有到8点的以8点计算,要求获得所有用户去窗口处理的等待时间的时间总和
思路:与1014题类似,构建一个结构体记录每个用户到达时间,离开时间,均用秒计算,随后排序用户到达时间,构建一个一维数组代表窗口,循环8时直到当前指定用户到达时间大于下午5点退出,循环每个窗口检查有没有完成了的,再看每个窗口有没有空的,空的就加入一个数,cur++,再循环一遍窗口前的用户有没有没有更新离开时间的,将离开时间更新为当前时间加办事时间。
随后计算记录的用户等待总时长,很明显,已经在窗口前的用户数量为cur,使用时间在前面更新离开时间的时候可以一起计算,等待时间为当前时间减去到达时间;
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n,k;//消费者和窗口数量
const int N=1e4+10;
struct customer{
int hh,mm,ss,tt, process_time;
int leave_time;
}cus[N];//tt为到达时间,process_time办事时间,leavetime为离开时间
bool cmp(customer A,customer B){
return A.tt<B.tt;
}//重写排序方式
int main(){
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++){
char c;
int h,m,s,pt;
cin>>h>>c>>m>>c>>s>>pt;
cus[i].hh=h;
cus[i].mm=m;
cus[i].ss=s;
cus[i].process_time=pt*60;
cus[i].tt=h*60*60+m*60+s;
}//输入处理
cus[n].tt=0x3f3f3f3f;//将最后一项的到达啥时间为无穷
//否则如果输入一直满足就会死循环
sort(cus,cus+n,cmp);//排序到达时间
int window[k];
for(int i=0;i<k;i++) window[i]=-1;//-1表示没人
int cur=0;
double sum=0;
for(int time=480*60;cus[cur].tt<=17*60*60;time++){
for(int i=0;i<k;i++){
if(window[i]!=-1&&cus[window[i]].leave_time==time){
window[i]=-1;
}
}
//出队
for(int i=0;i<k;i++){
if(window[i]==-1&&cus[cur].tt<=17*60*60&&cus[cur].tt<=time){
window[i]=cur++;
}
}
//入队
for(int i=0;i<k;i++){
if(window[i]!=-1&&cus[window[i]].leave_time==0){
sum+=time-cus[window[i]].tt;
cus[window[i]].leave_time=time+cus[window[i]].process_time;
}
}
//更新时间
}
printf("%.1lf",sum/cur/60);//计算的时间以秒为单位所以除以一个60
return 0;
}
P1018
题目链接:题目详情 - 1018 Public Bike Management (pintia.cn)
题目大意:C,N,P,M分别代表车站最大容量,车站数量,问题车站,边数。题目规定当前自行车的数量为车站容量一半的时候为完美状态,如果车站容量为0,或者满则为问题车站。现给定每个车站的初始车数,要求找到一条最短的路径,如果存在多个最短,则找到需要带的车最少的情况,如果还有多组就找到带走车最少的情况,以满足一路上的车站均为完美车站
思路:用邻接表建边,随后用深搜遍历一遍到达P点的每种路径,更行最短距离,如果当前距离是最短,则判段带来车数和带走车数。(备注:试过直接用sum来记录当前需要的车的总数,直接找最小,但是只拿了20分,说明会有带来x辆,带走x辆这种情况)
实现代码:
#include<bits/stdc++.h>
using namespace std;
int C,N,P,M;//最大容量,车站数量,问题车站,边数
int bike[510],g[510][510],dist[510];//当前车站车数,领接表,最短路径
vector<int> v[510];
vector<int> path;//存一条路径
int final_dist=0x3f3f3f3f,final_send,final_take;
vector<int> final_ans;//最终答案路径
void dfs(int x,int distance,int send,int take){
//当前点,走到当前点的距离,当前带来的车,当前带走的车
if(distance>dist[x]) return;
//如果当前路径比之前遍历过的大,及时止损
path.push_back(x);
//如果到了问题车站
if(x==P){
//判段是否满足最小,更新
if(distance<final_dist||(distance==final_dist&&send<final_send)||(distance==final_dist&&send==final_send&&take<final_take)){
final_ans=path;
final_dist=distance;
final_send=send;
final_take=take;
}
//更新最短路径
}else{
dist[x]=min(dist[x],distance);
//遍历这个点的其他邻接点
for(auto i: v[x]){
//更行send和take
if(take+bike[i]<C/2){
dfs(i,distance+g[x][i],send+C/2-take-bike[i],0);
}else{
dfs(i,distance+g[x][i],send,take+bike[i]-C/2);
}
}
}
path.pop_back();
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>C>>N>>P>>M;
for(int i=1;i<=N;i++) cin>>bike[i];
memset(g,0x3f,sizeof g);
while(M--){
int a,b,c;
cin>>a>>b>>c;
v[b].push_back(a);
v[a].push_back(b);
g[a][b]=g[b][a]=min(g[a][b],c);
}
memset(dist,0x3f,sizeof dist);
//初始化
dfs(0,0,0,0);
cout<<final_send<<" ";
for(auto i: final_ans){
if(i==0) cout<<i;
else cout<<"->"<<i;
}
cout<<" "<<final_take;
return 0;
}
备注:这里图总没有记录每个点是否被走过,但是在dfs的过程中,如果走到了当前点,在走回去的话,路径肯定是更小的,所以省略了
P1019
题目链接:题目详情 - 1019 General Palindromic Number (pintia.cn)
题目大意:给定一个N,一个b,检测N在b进制下是否是回文数,输出Yes/No,再讲N在b进制下的表示输出
思路:进制转换一次,回文判段一次,倒过来输出一次
实现代码:
#include<bits/stdc++.h>
using namespace std;
int N,b;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>N>>b;
vector<int> v;
//进制转换
while(N){
v.push_back(N%b);
N/=b;
}
bool flag=true;
//回文判段
for(int i=0;i<v.size()/2;i++){
if(v[i]!=v[v.size()-i-1]){
flag=false;
break;
}
}
if(flag) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
cout<<v[v.size()-1];
for(int i=v.size()-2;i>=0;i--){
cout<<" "<<v[i];
}
return 0;
}
P1020
题目链接:题目详情 - 1020 Tree Traversals (pintia.cn)
题目大意:给出二插树的后续遍历和中序遍历,要求输出层序遍历
思路:后续遍历中的最后一个为根节点,找到他在中序遍历中的位置,获得他的左右区间,同理带回后续遍历中去,每个区间的最后一个为这个区间的根节点。最后用数组模拟队列,遍历一遍(也可以不用数组吧,这里直接输出就好了的)
实现代码:
#include<bits/stdc++.h>
using namespace std;
int N,post[40],in[40];
struct node{
int value;
node *left;
node *right;
};
//二叉树构建
node *makenode(int h1,int t1,int h2,int t2){
if(h1>t1) return NULL;
node *p=new node;
p->value=post[t1];
int idx;
for(idx=h2;in[idx]!=post[t1];idx++);//找到这个点的在中序遍历中的位置
p->left=makenode(h1,idx-1-h2+h1,h2,idx-1);//两个区间长度是相同的,所以这里用中序遍历找后续遍历长度,相减一下就能获得终点位置
p->right=makenode(idx-h2+h1,t1-1,idx+1,t2);
return p;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>N;
for(int i=0;i<N;i++) cin>>post[i];
for(int i=0;i<N;i++) cin>>in[i];
node *root=makenode(0,N-1,0,N-1);//两个区间的起点和终点
node *q[40];
int hh=0,tt=-1;
q[++tt]=root;
while(hh<=tt){
if(hh!=0) cout<<" ";
node *p=q[hh++];
cout<<p->value;
if(p->left) q[++tt]=p->left;
if(p->right) q[++tt]=p->right;
}
return 0;
}
P1021
题目链接:题目详情 - 1021 Deepest Root (pintia.cn)
题目大意:首先要判段是否是一颗树,如果不是则按要求输出,如果是,则要求找到从一个结点开始算,深度最深的所有结点
思路:
1)判段是否是一棵树,则需要对所有点dfs一次,当然如果在一次dfs中遍历过了,则直接跳过就好,如果只遍历了一次,那么说明是连同的,则是一棵数
2)要找到深度最深的点,不妨从两边进行判段,例如从左边任意找一个点,这里直接用点1即可,不管他是不是最深的,可以找到从左边到右边距离最大的点,这些距离最大的点一定是最深的,再从这些点往左找,再次去找最深,即可
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n;
vector<int> v[N];//邻接表
vector<int> longest;//存最远的点
set<int> ans;//存答案,且按升序排列
int maxdepth=0;//最大深度
bool st[N];
void dfs(int x){
st[x]=true;
for(int i=0;i<v[x].size();i++){
int j=v[x][i];
if(!st[j]) dfs(j);
}
}//第一遍dfs判段是否是树
void dfs2(int x,int depth){
//判段是否被遍历过
if(st[x]) return;
st[x]=true;
//发现当前点深度更大,则更新最大深度
if(depth>maxdepth){
maxdepth=depth;
longest.clear();
longest.push_back(x);
}else if(depth==maxdepth){
//相等则说明有多组解,加入数组
longest.push_back(x);
}
//继续遍历他的邻接结点
for(int i: v[x]){
dfs2(i,depth+1);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
v[a].push_back(b);
v[b].push_back(a);
}//建表
int cnt=0;
for(int i=1;i<=n;i++){
if(!st[i]){
cnt++;
dfs(i);
}
}
memset(st,0,sizeof st);
//判段是否是树
if(cnt!=1){
cout<<"Error: "<<cnt<<" components";
}else{
//第一遍从左边往右边走,找到右边距离最大的点
dfs2(1,0);
//把这些点保存进答案容器
for(auto i: longest) ans.insert(i);
//再次初始化,在右边的点中随意找一个点,往左走,找到往左边最深的点
longest.clear();
maxdepth=0;
memset(st,0,sizeof st);
dfs2(*ans.begin(),0 );
for(auto i: longest) ans.insert(i);
//输出答案
for(int i: ans){
cout<<i<<endl;
}
}
return 0;
}
P1022
题目链接:题目详情 - 1022 Digital Library (pintia.cn)
题目大意:图书馆里有很多本书,没本书包含了ID,title,author,很多关键词key,publisher,发行日期year,要求按照输入1,2,3,4,5代表title,author,key,,publisher,year以及给出相应信息,按照升序给出符合条件的所有ID
思路:其实这道题比较考察输入问题,简单的建一个哈希表就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
map<string,set<string>> mp[6];//1 title,2 author,3 key,4 publisher,5 year
int main(){
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
getchar();//使用getchar吃掉一个空格
while(n--){
//输入处理
string id,title,author,line,publisher,year;
string key;
getline(cin,id);
getline(cin,title);
mp[1][title].insert(id);
getline(cin,author);
mp[2][author].insert(id);
getline(cin,line);
//使用stringstream处理一行的多个key,可以看下我之前写的文章
stringstream ss(line);
while(ss>>key){
mp[3][key].insert(id);
}
getline(cin,publisher);
mp[4][publisher].insert(id);
getline(cin,year);
mp[5][year].insert(id);
}
int k;
cin>>k;
while(k--){
int x;
cin>>x;
//吃掉一个:和一个空格
getchar();
getchar();
string s;
getline(cin,s);
cout<<x<<": "<<s<<endl;
if(mp[x].count(s)){
for(auto t: mp[x][s]){
cout<<t<<endl;
}
}else{
cout<<"Not Found"<<endl;
}
}
return 0;
}
P1023
题目链接:题目详情 - 1023 Have Fun with Numbers (pintia.cn)
题目大意:给定一个最长为20位的数,要求这个数的两倍是否是这个数的另一种排列?
思路:这道题建议用java或者python处理做大数处理更加方便,处理完成之后用一个哈希表去跑一边就可以了
实现代码:
import java.io.*;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
public class Main {
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
public static void main(String[] args) throws IOException {
String a=bf.readLine();
BigInteger A=new BigInteger(a);
Map<Integer,Integer> map=new HashMap<>();
//输入操作,添加一个哈希表
for(int i=0;i<a.length();i++) {
int k=a.charAt(i)-'0';
if(map.containsKey(k)) {
map.put(k,map.get(k)+1);
}else {
map.put(k,1);
}
}
//建立哈希表
A=A.multiply(BigInteger.TWO);//翻倍
a=A.toString();
boolean flag=true;
//遍历一遍两倍后的数组
for(int i=0;i<a.length();i++) {
int j=a.charAt(i)-'0';
if(map.containsKey(j)&&map.get(j)>0) {
map.put(j, map.get(j)-1);
}else {
flag=false;
break;
}
}
if(flag) {
pw.println("Yes");
}else {
pw.println("No");
}
pw.print(a);
pw.flush();
}
}
P1024
题目链接:题目详情 - 1024 Palindromic Number (pintia.cn)
题目大意:给定一个数N,给定最大操作次数K,每次操作可以将N反过来加回到N上,如果N是回文数,则直接输出N和其操作倍数,一直最后都不是回文则输出最后的N和K
思路:这道题还是用java或者python来做,因为N的起始最大值就为10的10次方,所以最终结果可能非常大,则需要大整数操作,操作步骤就非常简单了,按要求计算即可
实现代码:
import java.io.*;
import java.math.BigInteger;
public class Main {
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
public static void main(String[] args) throws IOException {
String[] temp=bf.readLine().split(" ");
BigInteger a=new BigInteger(temp[0]);
int k=Integer.parseInt(temp[1]);
String N=temp[0];
//k为最大倍数,N为当前数,a为当前数的BigInteger类型表示
for(int i=0;i<k;i++) {
char[] arr=N.toCharArray();
if(check(arr)) {
k=i;
break;
}else {
String other="";
for(int j=arr.length-1;j>=0;j--) {
other+=arr[j];
}
a=a.add(new BigInteger(other));
N=a.toString();
}
}
pw.println(N);
pw.print(k);
pw.flush();
}
//判段回文
public static boolean check(char[] arr) {
int len=arr.length;
for(int i=0;i<len/2;i++) {
if(arr[i]!=arr[len-i-1]) return false;
}
return true;
}
}
P1025
题目链接:题目详情 - 1025 PAT Ranking (pintia.cn)
题目大意:给定n个赛场,每个赛场给出k名选手信息,包含选手考号和选手成绩,要求先输出总共有多少考生,然后输出每个考生总排名,考场号,和考场排名
思路:用两个哈希表分别维护分考场排名和分考场考号,随后要重写排序,因为分数从大到小,考生号要从小到达排名,这里要处理一下分数重复情况,相同分数要和前面的排名一样
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,string> PIS;
vector<PIS> pati[110];//分考场排名
vector<PIS> All;//总排名
map<string,int> mp;//组号
map<string,int> mp2;//组排名
int ranklist[30010];//记录总排名
bool cmp(PIS A,PIS B){
if(A.first!=B.first) return A.first>B.first;
else return A.second<B.second;
}//重写排序
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
int cnt=0;//总和
for(int i=1;i<=n;i++){
int k;
cin>>k;
cnt+=k;
while(k--){
//输入处理,维护考场信息
string s;
int score;
cin>>s>>score;
pati[i].push_back({score,s});
All.push_back({score,s});
mp[s]=i;
}
}
//对每个考场排序并且生成排名
for(int i=1;i<=n;i++){
sort(pati[i].begin(),pati[i].end(),cmp);
for(int j=0;j<pati[i].size();j++){
auto t=pati[i][j];
if(j!=0&&t.first==pati[i][j-1].first) mp2[t.second]=mp2[pati[i][j-1].second];
else mp2[t.second]=j+1;
}
}
//总排名,处理成绩相同情况
sort(All.begin(),All.end(),cmp);
cout<<cnt<<endl;
for(int i=0;i<cnt;i++){
if(i!=0&&All[i].first==All[i-1].first) ranklist[i]=ranklist[i-1];
else ranklist[i]=i+1;
cout<<All[i].second<<" "<<ranklist[i]<<" "<<mp[All[i].second]<<" "<<mp2[All[i].second]<<endl;
}
return 0;
}
P1026
题目链接:题目详情 - 1026 Table Tennis (pintia.cn)
题目大意:给定n个玩家信息,每个信息包括到达时间和游玩时间,要求如果游玩时间超过2小时要以2小时计算,并且给定是否是vip,再给定k个桌子,有m个会员桌,下一行输入m个会员桌的序号。会员可以排会员队和非会员队,而非会员只能排非会员队,但是非会员可以用会员桌和非会员桌,而会员只能用会员桌;
思路:还是和银行的题一样,把他们的到达时间和服务时间还有离开时间遍历一遍时间来判段,首先判段每个桌子前是否有人,再判段他的离开时间是否是当前时间,再开两个队列,分别存放会员和非会员,将当前时间到达了的人加入两个队列排队,最后是服务,首先看会员队,因为会员在两个队伍中都入队了,每次开始前要检查一遍是否重复,如果当前这个人已经更新过服务时间了说明他在另一个对已经更新过了,就把这个人从队伍中pop,随后就是更新操作,更新队前每个人的服务时间和离开时间
备注:这题没有完全过,贴个代码过段时间回来更新(好坑啊好坑啊,好繁琐,还有为什么会员只能玩会员桌?。。。)
实现代码:
#include<bits/stdc++.h>
using namespace std;
struct player{
int come_time,play_time,serve_time=0,leave_time;//到达时间,玩的时间,到达服务时间,离开时间
int isvip;
}p[10010];//从0开始记
struct table{
int count,serve=-1;//服务过多少人,当前正在服务哪个人
int isvip=0;
}t[110];//从1开始记
vector<int> ans;
bool cmp(player A,player B){
return A.come_time<B.come_time;
}
void print_time(int time){
printf("%02d:%02d:%02d ",time/3600,time%3600/60,time%60);
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
int hh,mm,ss;
char c;
cin>>hh>>c>>mm>>c>>ss;
p[i].come_time=hh*60*60+mm*60+ss;
cin>>p[i].play_time;
p[i].play_time*=60;
if(p[i].play_time>2*60*60) p[i].play_time=2*60*60;
cin>>p[i].isvip;
}
int k,m;//桌子的数量和vip桌子的数量
cin>>k>>m;
while(m--){
int x;
cin>>x;
t[x].isvip=1;
}
sort(p,p+n,cmp);
int cur=0;//当前指到的人
queue<int> q1,q2;//普通队,会员队
for(int time=28800;time<=75600;time++){
//送客
for(int i=1;i<=k;i++){
if(t[i].serve>=0){
int j=t[i].serve;//获得他正在服务的人
if(p[j].leave_time==time){
t[i].serve=-1;
}
}
}
//入队
while(cur<n&&p[cur].come_time==time){
q1.push(cur);
if(p[cur].isvip) q2.push(cur);
cur++;
}
//服务
//检查会员队的人员是否有在非会员队已经出队了的
while(q2.size()&&p[q2.front()].serve_time!=0) q2.pop();
for(int i=1;i<=k;i++){
if(t[i].isvip&&t[i].serve==-1&&q2.size()){
int j=q2.front();
t[i].serve=j;
t[i].count++;
ans.push_back(j);
p[j].serve_time=time;
p[j].leave_time=time+p[j].play_time;
while(q2.size()&&p[q2.front()].serve_time!=0) q2.pop();
}
}
//检查非会员队是否有在会员队已经出队了的
while(q1.size()&&p[q1.front()].serve_time!=0) q1.pop();
for(int i=1;i<=k;i++){
if(t[i].serve==-1&&q1.size()){
int j=q1.front();
t[i].serve=j;
t[i].count++;
ans.push_back(j);
p[j].serve_time=time;
p[j].leave_time=time+p[j].play_time;
while(q1.size()&&p[q1.front()].serve_time!=0) q1.pop();
}
}
}
for(int i=0;i<ans.size();i++){
int j=ans[i];
print_time(p[i].come_time);
print_time(p[i].serve_time);
cout<<(p[i].serve_time-p[i].come_time+30)/60<<endl;;
}
for(int i=1;i<=k;i++){
if(i!=1) cout<<" ";
cout<<t[i].count;
}
return 0;
}
P1027
题目链接:题目详情 - 1027 Colors in Mars (pintia.cn)
题目大意:将给的3个数转换成13进制
思路:不会超过169,直接做一次判段就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
void print(int a){
if(a>9) cout<<(char)('A'+a-10);
else cout<<a;
}
int main(){
cout<<"#";
for(int i=0;i<3;i++){
int j;
cin>>j;
print(j/13);
print(j%13);
}
return 0;
}
P1028
题目链接:题目详情 - 1028 List Sorting (pintia.cn)
题目大意:输入n组数据和一个判段k,每个数据包含id,name和score,如果k为1,按照id排序,如果k为2按照name排序,如果k为3按照score排序,当然排序项相同时,前面做升序
思路:写三个排序判段一下就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct student{
string id;
string name;
int grade;
}stu[N];
//三种排序
bool cmp1(student A,student B){
return A.id<B.id;
}
bool cmp2(student A,student B){
if(A.name!=B.name) return A.name<B.name;
return A.id<B.id;
}
bool cmp3(student A,student B){
if(A.grade!=B.grade) return A.grade<B.grade;
if(A.name!=B.name) return A.name<B.name;
return A.id<B.id;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,col;
cin>>n>>col;//人数,列数
for(int i=0;i<n;i++){
cin>>stu[i].id>>stu[i].name>>stu[i].grade;
}
//输入操作
if(col==1) sort(stu,stu+n,cmp1);
else if(col==2) sort(stu,stu+n,cmp2);
else sort(stu,stu+n,cmp3);
for(int i=0;i<n;i++){
cout<<stu[i].id<<" "<<stu[i].name<<" "<<stu[i].grade<<endl;
}
return 0;
}
P1029
题目链接:题目详情 - 1029 Median (pintia.cn)
题目大意:给定两个序列,找到这两个序列合并后的中位数
思路:很多写归并排序,但是这里直接一个sort就可以了吧,nlogn也不会超
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
vector<LL> v;
for(int i=0;i<2;i++){
int len;
cin>>len;
while(len--){
LL x;
cin>>x;
v.push_back(x);
}
}
int cur=(v.size()-1)/2;
sort(v.begin(),v.end());
cout<<v[cur];
return 0;
}
P1030
题目链接:题目详情 - 1030 Travel Plan (pintia.cn)
题目大意:给定n个点,和m条边,规定每条边的信息包括,两个点之间的距离,和到达的花费价格,给定起点和终点,要求求出从起点到终点的最小距离如果存在多组解,则比较最小花费
思路:dfs遍历一遍,比较一下就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=510;
vector<int> v[N];
int g[N][N],c[N][N];
int n,m,s,d;//城市数量,道路数量,起点,终点
bool st[N];
int dist[N],co[N];
vector<int> ans;//最终答案路劲
vector<int> path;//储存一条路径
int mindist=0x3f3f3f3f,mincost=0x3f3f3f3f;
void dfs(int x,int distance,int cost){
if(distance>dist[x]) return;//比之前的大跳过
path.push_back(x);//保存更新路径
if(x==d){//更新最短路径
if(distance<mindist||(distance==mindist&&cost<mincost)){
mindist=distance;
mincost=cost;
ans=path;
}
}else{//如果没有到达则遍历相邻点
dist[x]=min(dist[x],distance);
for(auto i: v[x]){
dfs(i,distance+g[x][i],cost+c[x][i]);
}
}
path.pop_back();
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>s>>d;
memset(g,0x3f,sizeof g);
memset(c,0x3f,sizeof c);
memset(dist,0x3f,sizeof dist);
while(m--){
int a,b,cc,d;
cin>>a>>b>>cc>>d;
v[a].push_back(b);
v[b].push_back(a);
g[a][b]=g[b][a]=min(g[a][b],cc);
c[a][b]=c[b][a]=min(c[a][b],d);
}
dfs(s,0,0);
for(auto i: ans){
cout<<i<<" ";
}
cout<<mindist<<" "<<mincost;
return 0;
}
P1031
题目链接:题目详情 - 1031 Hello World for U (pintia.cn)
题目大意:给一个字符串,让其按U型排列
思路:为了避免麻烦,直接开数组好了
实现代码:
#include<bits/stdc++.h>
using namespace std;
char g[100][100];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string s;
cin>>s;
int len=s.length();
int n1,n2,n3;
int cur=0;
n1=n3=(len+2)/3;//长度计算
n2=len-n1-n3;//3
//按顺序赋值
for(int i=0;i<n1;i++){
for(int j=0;j<n2+2;j++){
g[i][j]=' ';
}
}
for(int i=0;i<n1;i++){
g[i][0]=s[cur++];
}
for(int i=1;i<n2+2;i++){
g[n1-1][i]=s[cur++];
}
for(int i=n1-2;i>=0;i--){
g[i][n2+1]=s[cur++];
}
for(int i=0;i<n1;i++){
for(int j=0;j<n2+2;j++){
cout<<g[i][j];
}
cout<<endl;
}
return 0;
}
P1032
题目链接:题目详情 - 1032 Sharing (pintia.cn)
题目大意:给定每个结点的地址,价值和下一个结点地址,要求找到这两个链表相同链的起点
思路:直接遍历第一个链表的地址,保存,从第二个开始遍历,如果这个地址在第一个链表中出现过,那么后面肯定是相同的直接输出即可
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char e[N];
int ne[N];
int main(){
int start1,start2,n;
cin>>start1>>start2>>n;
//建立链表
while(n--){
int address,Ne;
char c;
cin>>address>>c>>Ne;
e[address]=c;
ne[address]=Ne;
}
set<int> A;//储存第一个序列遍历过的地址
for(int i=start1;i!=-1;i=ne[i]){
A.insert(i);
}
for(int i=start2;i!=-1;i=ne[i]){
if(A.count(i)){
printf("%05d",i);//要按5位输出
return 0;
}
}
cout<<"-1";
return 0;
}
P1034
题目链接:题目详情 - 1034 Head of a Gang (pintia.cn)
题目大意:给定n个人,和通话下限k,随后n行输入两个名字和他们的通话时长,如果有3个人及以上互相通话,且通话时长达到下限k,则说明他们是一个团伙,要求输出团伙总数,随后每行输出每个团伙的老大,和他们的团伙总人数
思路:看这道题就想到dfs去遍历,但是名字是字符串类型,所以不好做遍历,这里就用哈希表去储存每个名字的映射,然后再用dfs去遍历每个点,这里注意不是每个循环到的区块都能成为一个团伙,所以要先判断,再对下标++
实现代码:
#include<bits/stdc++.h>
using namespace std;
struct gang{
vector<int> member;
string head;
int total_time;
}g[2010];
int n,k;
unordered_map<string,int> mp1;//名字到编号映射
unordered_map<int,string> mp2;//编号到名字映射
vector<int> v[2010];//储存点与点之间的邻接关系
int minute[2010];//储存每个点的总电话时长
bool st[2010];//储存是否遍历过
int gang_num=0;//团伙总数
void dfs(int x){
if(st[x]) return;
st[x]=true;
g[gang_num].member.push_back(x);
g[gang_num].total_time+=minute[x];
for(auto t: v[x]){
dfs(t);
}
}
bool cmp(gang A,gang B){
return A.head<B.head;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++){
string name1,name2;
int time;
cin>>name1>>name2>>time;
int cur1,cur2;
//名字映射
if(mp1.count(name1)){
cur1=mp1[name1];
}else{
cur1=mp1[name1]=mp1.size()+1;
}
if(mp1.count(name2)){
cur2=mp1[name2];
}else{
cur2=mp1[name2]=mp1.size()+1;
}
mp2[cur2]=name2;
mp2[cur1]=name1;
v[cur1].push_back(cur2);
v[cur2].push_back(cur1);
minute[cur1]+=time;
minute[cur2]+=time;
}
//遍历每个点
for(auto t: mp1){
if(!st[t.second]){
g[gang_num].member.clear();
g[gang_num].total_time=0;
dfs(t.second);
//如果满足构成团伙条件,更新团伙老大和下标
if(g[gang_num].member.size()>2&&g[gang_num].total_time>k*2){
int maxminute=0,head;
for(auto t:g[gang_num].member){
if(minute[t]>maxminute){
maxminute=minute[t];
head=t;
}
}
g[gang_num].head=mp2[head];
gang_num++;
}
}
}
//要求按照名字升序排列
sort(g,g+gang_num,cmp);
cout<<gang_num<<endl;
for(int i=0;i<gang_num;i++){
cout<<g[i].head<<" "<<g[i].member.size()<<endl;
}
return 0;
}
P1035
题目链接:题目详情 - 1035 Password (pintia.cn)
题目大意:
按这个要求更换字符串输出即可
思路:把错误的字符串加进去,注意总数为1的情况特判一下
实现代码:
#include<bits/stdc++.h>
using namespace std;
struct account{
string name;
string pass;
};
vector<account> v;//储存问题账户
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
string name,pass;
cin>>name>>pass;
bool flag=true;//判段是否要改
for(int i=0;i<pass.length();i++){
if(pass[i]=='1'){
pass[i]='@';
flag=false;
}else if(pass[i]=='0'){
pass[i]='%';
flag=false;
}else if(pass[i]=='l'){
pass[i]='L';
flag=false;
}else if(pass[i]=='O'){
pass[i]='o';
flag=false;
}
}
if(!flag) {
account A;
A.name=name;
A.pass=pass;
v.push_back(A);
}
}
if(v.size()){
cout<<v.size()<<endl;
for(int i=0;i<v.size();i++){
cout<<v[i].name<<" "<<v[i].pass<<endl;
}
}else{
if(n==1){
printf("There is 1 account and no account is modified");
}else{
printf("There are %d accounts and no account is modified",n);
}
}
return 0;
}
P1036
题目链接:题目详情 - 1036 Boys vs Girls (pintia.cn)
题目大意:给定男孩或者女孩名字,性别,id,分数,要求输出男孩最高分,女孩最低分,再输出差值,如果有一种类没有,就在相应行输出absent,最后输出NA
思路:件两个vector排序一下就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
struct people{
string name;
string gender;
string id;
int grade;
};
vector<people> f;
vector<people> m;
bool cmp1(people A,people B){//female排序
return A.grade>B.grade;
}
bool cmp2(people A,people B){
return A.grade<B.grade;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
while(n--){
string name,gender,id;
int grade;
cin>>name>>gender>>id>>grade;
people A={name,gender,id,grade};
//种类加入
if(gender=="M"){
m.push_back(A);
}else{
f.push_back(A);
}
}
//排序
sort(f.begin(),f.end(),cmp1);
sort(m.begin(),m.end(),cmp2);
//按要求输出
if(f.size()&&m.size()){
cout<<f[0].name<<" "<<f[0].id<<endl;
cout<<m[0].name<<" "<<m[0].id<<endl;
cout<<f[0].grade-m[0].grade;
}else{
if(f.size()){
cout<<f[0].name<<" "<<f[0].id<<endl;
cout<<"Absent"<<endl;
}else if(m.size()){
cout<<"Absent"<<endl;
cout<<m[0].name<<" "<<m[0].id<<endl;
}
cout<<"NA"<<endl;
}
return 0;
}
P1037
题目链接:题目详情 - 1037 Magic Coupon (pintia.cn)
题目大意:给定两组数据,要求两个数组中各项相乘最大值,可以选择省略不要
思路:两个数组排序后从前往后找负数,再从后往前找正数,相加就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int a[N],b[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n1,n2;
cin>>n1;
for(int i=0;i<n1;i++) cin>>a[i];
cin>>n2;
for(int i=0;i<n2;i++) cin>>b[i];
LL sum=0;
//数据输入
sort(a,a+n1);
sort(b,b+n2);
//排序
for(int i=0,j=0;i<n1&&j<n2&&a[i]<0&&b[j]<0;i++,j++){
sum+=a[i]*b[j];
}
for(int i=n1-1,j=n2-1;i>=0&&j>=0&&a[i]>0&&b[j]>0;i--,j--){
sum+=a[i]*b[j];
}
//分别从前往后和从后往前
cout<<sum;
return 0;
}
P1038
题目链接:题目详情 - 1038 Recover the Smallest Number (pintia.cn)
题目大意:给定n个字符串,求其拼接起来的大小最小值
思路:不管怎么变化,长度都是相同的,所以将每个字符串按照字符顺序排序一遍就可以了,这里要注意特判一下全为0的情况
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
string s[N];
bool cmp(string A,string B){
return A+B<B+A;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
bool flag=true;//判段是否全为0,不是则为false
for(int i=0;i<n;i++) {
cin>>s[i];
if(s[i]=="0") flag=false;
}
sort(s,s+n,cmp);
//特判0情况
if(!flag) cout<<"0"<<endl;
else{
//只用转第一个数去掉前导0
if(n>0) cout<<atoi(s[0].c_str());
for(int i=1;i<n;i++){
cout<<s[i];
}
}
return 0;
}
P1039
题目链接:题目详情 - 1039 Course List for Student (pintia.cn)
题目大意:给定n个学生和k门课程,随后k组信息,分别给定课程id,和选这门课程的人,随后一行给要查分数的人,要求按升序输出选的课程
思路:建一个哈希表储存,最后输出的时候排序一下就好了
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n,k;//学生人数,课程数
map<string,vector<int>> mp;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>k;
//输入操作,建立哈希表
while(k--){
int id,num;
cin>>id>>num;
while(num--){
string name;
cin>>name;
mp[name].push_back(id);
}
}
//按要求输出,输出的时候排序一下
while(n--){
string name;
cin>>name;
cout<<name<<" "<<mp[name].size();
sort(mp[name].begin(),mp[name].end());
for(auto t: mp[name]){
cout<<" "<<t;
}
cout<<endl;
}
return 0;
}
P1040
题目链接:题目详情 - 1040 Longest Symmetric String (pintia.cn)
题目大意:找一条字符串的回文子串最长的长度
思路:一看还在想什么dp写法,想想pat25分题的难度,再看看数据类型,好的直接暴力做就是了
实现代码:
#include<bits/stdc++.h>
using namespace std;
//判段回文函数
bool check(string s){
int len=s.length();
for(int i=0;i<len/2;i++){
if(s[i]!=s[len-i-1]) return false;
}
return true;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string s;
getline(cin,s);
int mxlen=0;
int len=s.length();
for(int i=0;i<len;i++){
for(int j=i+1;j<=len;j++){
string temp=s.substr(i,j);
if(check(temp)){
int templen=temp.length();
mxlen=max(mxlen,templen);
}
}
}
cout<<mxlen<<endl;
return 0;
}
P1041
题目链接:题目详情 - 1041 Be Unique (pintia.cn)
题目大意:找到第一个只出现一次的数字
思路:走一遍保存,再走一遍判段就是了
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
unordered_map<int,int> mp;
int arr[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>arr[i];
mp[arr[i]]++;
}
bool flag=true;//默认有解
for(int i=0;i<n;i++){
if(mp[arr[i]]==1){
cout<<arr[i];
flag=false;
break;
}
}
if(flag) cout<<"None"<<endl;
return 0;
}
P1042
题目链接:题目详情 - 1042 Shuffling Machine (pintia.cn)
题目大意:给定一个数组,要求按照数组指的值换顺序,k次
思路:实现并不难,但是要注意最好是将当前位置的值赋值给arr的第i位,如果用当前数组的第arr[i]位赋值给当前数组,这种做法就是错误的
实现代码:
#include<bits/stdc++.h>
using namespace std;
int arr[100];//换的位置,答案数组
map<int,string> mp;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
//建立排号
for(int i=1;i<=13;i++) mp[i]=("S"+to_string(i));
for(int i=14;i<=26;i++) mp[i]=("H"+to_string(i-13));
for(int i=27;i<=39;i++) mp[i]=("C"+to_string(i-26));
for(int i=40;i<=52;i++) mp[i]=("D"+to_string(i-39));
mp[53]="J1",mp[54]="J2";
int origin[100];
for(int i=1;i<=54;i++){
cin>>arr[i];
origin[i]=i;
}
while(n--){
int temp[100];
for(int i=1;i<=54;i++){
temp[arr[i]]=origin[i];
}
for(int i=1;i<=54;i++){
origin[i]=temp[i];
}
}
for(int i=1;i<=54;i++){
if(i!=1) cout<<" ";
cout<<mp[origin[i]];
}
return 0;
}
P1043
题目链接:题目详情 - 1043 Is It a Binary Search Tree (pintia.cn)
题目大意:给定一个n,和一个数组,要求判段这个数组是二叉搜索树的先序遍历还是镜像二插搜索树的后续遍历
思路:考察二叉树的建立相关知识,我尝试用数组去模拟这种建树,但是好像我做不到,所以我这里直接写了,如果建立二插搜索树,树的左边是小于根节点,右边大于根节点的,所以我们只要从当前下标往后找,比他小的第一个连续范围即他的左树,那后面必然是一个比他大的连续范围,且每个范围的第一个结点为他的根节点,因为假设是先序遍历。最后如果发现建树得到的结点数比n小,那就换另一种去尝试,如果两种都不行,那就输出NO
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[1010];
int cnt=0;
vector<int> v;
struct tree{
int value;
tree *l,*r;
};
//构建二插搜索树
tree* makeBst(int cur,int ceil,int floor){
//当前点的下标,上下限
tree *p=new tree;
p->value=a[cur];
cnt++;
int i;
for(i=cur+1;i<n&&a[i]<a[cur]&&a[i]>=floor;i++);
if(i==cur+1) p->l=NULL;
else p->l=makeBst(cur+1,a[cur],floor);
if(i<n&&a[i]<ceil&&a[i]>=a[cur]){
p->r=makeBst(i,ceil,a[cur]);
}else{
p->r=NULL;
}
return p;
}
//后续遍历
void past_order(tree *p){
if(!p) return;
past_order(p->l);
past_order(p->r);
v.push_back(p->value);
}
//镜像二插搜索树,这里其实基本复制一下,改一下每个上下限就可以了
tree* makeMBst(int cur,int ceil,int floor){
//当前点的下标,上下限
tree *p=new tree;
p->value=a[cur];
cnt++;
int i;
for(i=cur+1;i<n&&a[i]<ceil&&a[i]>=a[cur];i++);
if(i==cur+1) p->l=NULL;
else p->l=makeMBst(cur+1,ceil,a[cur]);
if(i<n&&a[i]>=floor&&a[i]<a[cur]){
p->r=makeMBst(i,a[cur],floor);
}else{
p->r=NULL;
}
return p;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
tree *root=makeBst(0,0x3f3f3f3f,-0x3f3f3f3f);
if(cnt==n){
cout<<"YES"<<endl;
past_order(root);
for(int i=0;i<v.size();i++){
if(i!=0) cout<<" ";
cout<<v[i];
}
}else{
cnt=0;
root=makeMBst(0,0x3f3f3f3f,-0x3f3f3f3f);
if(cnt==n){
cout<<"YES"<<endl;
past_order(root);
for(int i=0;i<v.size();i++){
if(i!=0) cout<<" ";
cout<<v[i];
}
}else{
cout<<"NO"<<endl;
}
}
return 0;
}
P1044
题目链接:题目详情 - 1044 Shopping in Mars (pintia.cn)
题目大意:给定一连串的值,要求找到一个区间{i,j}的值等于给定值total,若存在多组解,要求按照i的顺序输出,如果没有则输出区间和最小的解
思路:我用的双指针加前缀和,可能会有些麻烦,但是思路还是比较清晰的。用前缀和找到当前这一段的和,判段当前和如果大于total的话,再判段一下是否已经找到了相等的解了,如果没找到,我们就要更新一下,然后再往下一个循环走,如果找到了,就把flag设为true,加入答案
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int> PII;
int a[N];//储存每块钻石价值,1位起点
int sum[N];//前缀和
int mn=0x3f3f3f3f;
bool flag=false;//判段是否找到
vector<PII> ans;//储存前后下标
vector<PII> pre;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,total;
cin>>n>>total;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
//输入和前缀和处理
for(int i=1,j=i;i<=n&&j<=n;j++){
//获得当前值
int now=sum[j]-sum[i-1];
if(now>total){
//大于的情况
if(flag){
//起点变成下一个,j与i同步
i++;
j=i-1;
continue;
}
//如果当前值比总和大但是比存的最大值小
if(now<mn){
pre.clear();
pre.push_back({i,j});
mn=now;
}else if(now==mn){
pre.push_back({i,j});
}
i++;
j=i-1;
}else if(now==total){
//如果找到了直接加入
flag=true;
ans.push_back({i,j});
i++;
j=i-1;
}
}
if(flag){
for(auto t: ans){
cout<<t.first<<"-"<<t.second<<endl;
}
}else{
for(auto t: pre){
cout<<t.first<<"-"<<t.second<<endl;
}
}
return 0;
}
P1045
题目链接:题目详情 - 1045 Favorite Color Stripe (pintia.cn)
题目大意:要求按照给定顺序,找到给定数组的按照这个顺序的最大长度
思路:很像最长上升子序列问题,对于这类问题,我们通常有dp解法和二分解法,但是看看这个数据范围,所以直接用dp解法就好了,dp[i]的意思就是以i结尾的最大长度,我们没加入一个数,就可以看以前面所有数结尾的各种情况,找到他们对应的最大值+1,更新到最后一个就出来了,当然不一定最后一个最后,所以要找一遍(其实这里在更新的时候找也可以,但是简单整洁才是目的)
实现代码:
#include<bits/stdc++.h>
using namespace std;
int mp[210];
int dp[210];//以i结尾的最大数
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
int m,order[210];
//顺序数组
cin>>m;
//对应顺序
for(int i=1;i<=m;i++){
cin>>order[i];
mp[order[i]]=i;
}
int len,a[10010];
cin>>len;
//原数组
for(int i=1;i<=len;i++) cin>>a[i];
for(int i=1;i<=len;i++){
if(!mp[a[i]]) continue;
int j=mp[a[i]];//获得当前这个数在顺序中的位置
int mx=0;
for(int k=1;k<=j;k++){
mx=max(mx,dp[k]);
}
dp[j]=mx+1;
}
int ans=0;
for(int i=1;i<=m;i++){
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
P1046
题目链接:题目详情 - 1046 Shortest Distance (pintia.cn)
题目大意:给定n个数的数组,每个数分别代表i到i+1的距离,n代表n到1的距离,形成一个环,随后m个询问,找到i和j之间的最短路
思路:用前缀和找到两个数正面的距离,再用总和找一遍反面的距离比较输出即可
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int dist[N],sum[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
int total;//总共距离
for(int i=1;i<=n;i++){
cin>>dist[i];
sum[i]=sum[i-1]+dist[i];
total+=dist[i];
}
int m;
cin>>m;
while(m--){
int x,y;
cin>>x>>y;
if(x>y) swap(x,y);
int mn=sum[y-1]-sum[x-1];
mn=min(mn,total-mn);
cout<<mn<<endl;
}
return 0;
}
P1047
题目链接:题目详情 - 1047 Student List for Course (pintia.cn)
题目大意:给定n个学生,m门课程,要求按顺序输出每门课程的id,报的学生数量,和每位学生名字,要求名字按字母升序输出
思路:本来建立了一个哈希表直接做,但是发现超时了,那就说明输入出问题了,换成printf和vector存就可以过去了
实现代码:
#include<bits/stdc++.h>
using namespace std;
char stu[40010][5];//创建学生数组
vector<int> course[2510];
bool cmp(int a,int b){
return strcmp(stu[a],stu[b])<0;
}
int main(){
int n,m;
cin>>n>>m;//学生数,课程数
for(int i=1;i<=n;i++){
int class_num;
scanf("%s%d",stu[i],&class_num);
while(class_num--){
int class_id;
scanf("%d",&class_id);
course[class_id].push_back(i);
}
}
for(int i=1;i<=m;i++){
printf("%d %d\n",i,course[i].size());
sort(course[i].begin(),course[i].end(),cmp);
for(auto t: course[i]){
printf("%s\n",stu[t]);
}
}
return 0;
}
P1048
题目链接:题目详情 - 1048 Find Coins (pintia.cn)
题目大意:给定一个数组,要求找到两个数之和等于n,没有则输出No solution
思路:先排序,后双指针,注意这里如果小于就不要重置j的位置了,因为i往后走,值肯定是更大的,如果再小于前指针还会走,左指针往大,右指针往小
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,total;
cin>>n>>total;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
bool flag=true;
for(int i=0,j=n-1;i<n&&j>=0&&i<j;){
if(a[i]+a[j]>total){
j--;
//左走
}else if(a[i]+a[j]<total){
i++;
//右走
}else{
cout<<a[i]<<" "<<a[j]<<'\n';
flag=false;
break;
}
}
if(flag) cout<<"No Solution"<<'\n';
return 0;
}
P1049
题目链接:题目详情 - 1049 Counting Ones (pintia.cn)
题目大意:给定一个n,找到1-n中所有数包含1的个数,输出
思路:暴力遍历一遍肯定是会超时的,所以我们要用到分治(其实这是一道数位dp题吧。。。)
比如假设0-999有n个1,那么5000有多少个1呢?0-999,n个,2000-2999也是n个,3000-3999也是n个,4000-4999也是n个,但是1000-1999每个数前面都有1个1,所以是n加1000,总共是5n+1000。所以我们可以推出,比如1000,那么含有1的个数一定是100个数*10+100;
这道理用了笨办法,直接用数组好了每个数量级,每次递归找到当前数的数量级,再判段这个数是不是以1开头,不是的话就说明已经包含了所有以1开头的个数(具体思路可以看代码处)
实现代码:
#include<bits/stdc++.h>
using namespace std;
int a[]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
int b[]={0,1,20,300,4000,50000,600000,7000000,80000000,900000000};
int countone(int n){
if(!n) return 0;
int i;
for(i=0;n/a[i]>9;i++);//找到对应的数量级别
int j=n/a[i];
if(j==1){
return b[i]+1+countone(n-a[i])+n-a[i];
//例如1314,返回前1000的1的数量,加一个1到1000,最后找314中1的数量
//但是后面每个数前面都会带1,所以再加上1个314
}else{
return b[i]*j+a[i]+countone(n-j*a[i]);
//这里比如2314,没1000个数里面存在多少个1,但是这里包括了1000-1999
//所以要加上一个当前的数量级,然后继续往下递归
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
cout<<countone(n);
return 0;
}
P1050
题目链接:题目详情 - 1050 String Subtraction (pintia.cn)
题目大意:给定一组字符串,换掉给定的所有字符
思路:创建一个set数组作为字典,遍历一遍字符串有就不输出就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
set<char> st;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string s;
getline(cin,s);
string ne;
getline(cin,ne);
int len=ne.length();
for(int i=0;i<len;i++){
st.insert(ne[i]);
}
len=s.length();
for(int i=0;i<len;i++){
if(!st.count(s[i])){
cout<<s[i];
}
}
return 0;
}
P1051
题目链接:题目详情 - 1051 Pop Sequence (pintia.cn)
题目大意:给定一个长度最大为m的栈,随后数组长度为n,在给定k次询问,要求判段每次询问从1-n顺序输入,给出的数组是否可以通过这个栈输出
思路:分析一下每组数据,第一组数据无非是每次加入一个就pop()一次,那必然是可以的。
再看第二组数据,发现第一个为3,那明显这次不是顺序输入输出了,所以,我们可以吧1-3都加进去,才能完成第一次输出3,所以后面输出的是321,再往后,我们循环到的数的下标是3,下一个数是4,但是这里是7,也不是顺序输入输出了,所以这里就把4567按顺序加入进去。这个时候数组已经全部加完了,按要求输出7654,答案不符合,输出NO
第三组数据,如果第一个是7,那么说明要把1-7都先进去,但是栈的长度为5,不符合题意
第四组数据:第一个是5,我们可以吧1-5都加进去,然后将5输出,下一个数变成了6,第二个是6,那我们就把6也加进去,再输出,后面是43,就把栈顶的43输出,再是7,加入输出,最后输出21.符合条件
第五组数据:加入1,弹出,再加入2-7,也会超栈的长度,所以这里是NO
所以我们可以得出,如果当前遍历到的数,比我们记录的已经push过的次数(计算下一个数)还要大,那就把记录过的数往上加,知道栈的大小不够或者已经等于退出
此时可以判段,如果栈顶元素不是当前数,说明顺序不对,如过push过的数还是小于遍历到的数,那么说明栈的大小不够。最后判段一下这个循环有没有在中途break就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
int a[1010];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int m,n,k;//栈的长度,数组大小,询问次数
cin>>m>>n>>k;
while(k--){
stack<int> sk;
for(int i=0;i<n;i++) cin>>a[i];
int j=0;//代表已经push进数组多少次
int i;
for(i=0;i<n;i++){
//如果遇到<=j的数,那就说明已经在栈中了
while(sk.size()<m&&a[i]>j){
sk.push(++j);
}//遇到比当前数大的数,就从下标往上加到与当前数相等
//如果发现当前数还是不等于j,那就说明栈的大小不够
if(a[i]>j){
break;
//或者如果发现栈顶不是当前元素,即顺序不对
}else if(sk.top()!=a[i]){
break;
}else{
sk.pop();
}
}
if(i==n){
cout<<"YES"<<"\n";
}else{
cout<<"NO"<<"\n";
}
}
return 0;
}
P1052
题目链接:题目详情 - 1052 Linked List Sorting (pintia.cn)
题目大意:给定n个结点,提供每个结点的地址,值,下一个结点的地址,要求按值的顺序排列链表,按输入要求输出
思路:其实维护当前结点的大小和他的地址就可以了,按要求输出,要注意的是因为是用数组保存的地址,所以要输出5位数,还有一个点就是可能会出现不在链表中的数,所以要遍历一遍再加入容器中去,容器用PII类型保存就不用重写cmp了
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int e[N],ne[N];
vector<int> v;
//根据地址的值排序
bool cmp(int a,int b){
return e[a]<e[b];
}
int main(){
int n,head;
cin>>n>>head;
memset(ne,-1,sizeof ne);
while(n--){
int ad,value,ne_ad;
cin>>ad>>value>>ne_ad;
e[ad]=value;
ne[ad]=ne_ad;
}
//遍历一遍数组
for(int i=head;i!=-1;i=ne[i]){
v.push_back({i});
}
//为0情况
if(v.size()==0){
cout<<"0 -1"<<endl;
return 0;
}
sort(v.begin(),v.end(),cmp);
n=v.size();
//按要求输出
printf("%d %05d\n",n,v[0]);
for(int i=0;i<n;i++){
if(i==n-1){
printf("%05d %d %d\n",v[i],e[v[i]],-1);
}else{
printf("%05d %d %05d\n",v[i],e[v[i]],v[i+1]);
}
}
return 0;
}
P1053
题目链接:题目详情 - 1053 Path of Equal Weight (pintia.cn)
题目大意:要定一棵树,每个结点标有序号和权值,根节点为00结点,从求找到从根结点到叶子结点的一条路径的权值和为给定值,并且按照非递减顺序输出
思路:我的思路是将所有可行路径都加进一个容器,最后排序。用dfs往下走,写一个flag,如果没有dfs到下一个结点,说明是叶子结点,可以判段是否加入答案容器。
到最后重写cmp,比较一下就就可以了,我看到很多代码写的是直接在dfs之前按照大小排序,但是这种写法如果在两个点权值相等的情况下,比如10 3 2 6和 10 3 6 2,就会直接输出第一种情况,所以存在问题,可是。。。瞄的为什么我的也过不去,我好像不存在这种问题吧?
实现代码(29分):
#include<bits/stdc++.h>
using namespace std;
int n,m,s;//结点数量,非叶子结点数量,给定查找的权重
int w[110];
vector<int> v[110];//领接表
bool st[110];//判段当前结点有没有走过
vector<int> path;
vector<vector<int>> ans;//存结果情况
bool cmp(vector<int> A,vector<int> B){
int len=min(A.size(),B.size());
for(int i=0;i<len;i++){
if(A[i]!=B[i]) return w[A[i]]>w[B[i]];
}
}
void dfs(int now,int weight){
path.push_back(now);
st[now]=true;
bool flag=true;//为true则为叶子结点
for(auto t: v[now]){
if(!st[t]){
flag=false;
dfs(t,weight+w[t]);
}
}
if(flag&&weight==s){
ans.push_back(path);
}
path.pop_back();
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i=0;i<n;i++) cin>>w[i];
for(int i=0;i<m;i++){
int x,k;//当前结点和他的临界点大小
cin>>x>>k;
while(k--){
int temp;
cin>>temp;
v[x].push_back(temp);
}
}
dfs(0,w[0]);
sort(ans.begin(),ans.end(),cmp);
for(auto a: ans){
int len=a.size();
for(int i=0;i<len;i++){
if(i!=0) cout<<" ";
cout<<w[a[i]];
}
cout<<"\n";
}
return 0;
}
P1054
题目链接:题目详情 - 1054 The Dominant Color (pintia.cn)
题目大意:给定一个m行n列的矩阵,找到出现次数最多的数
思路:数据范围不大用一个哈希表储存一下遍历一遍就可以了,我这里用的string类型,怕超
实现代码:
#include<bits/stdc++.h>
using namespace std;
map<string,int> mp;
int m,n;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>m>>n;
//哈希表储存
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
string x;
cin>>x;
mp[x]++;
}
}
//更新
int mx=-1;
string ans;
for(auto t:mp){
if(t.second>mx){
ans=t.first;
mx=t.second;
}
}
cout<<ans;
return 0;
}
P1055
题目链接:题目详情 - 1055 The World's Richest (pintia.cn)
题目大意:给定n个人,k次询问,随后n行每行代表第i个人的姓名,年龄,财富。随后k行,每行有要查找的钱m名,最小年龄和最大年龄。要求输出k组数据,每组数据先输出case序号,再输出这个年龄段的钱m名富翁的名字,年龄,财富
思路:创建一个结构体排序一下就就可以了,然后询问的时候,遍历一遍数组找到前m个符合条件的输出就可,特判一下没有的情况
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,k;//n个人,k次询问
struct people{
string name;
int age,networth;
}p[N];
bool cmp(people A,people B){
if(A.networth!=B.networth) return A.networth>B.networth;
if(A.age!=B.age) return A.age<B.age;
return A.name<B.name;
}
int main(){
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>p[i].name>>p[i].age>>p[i].networth;
}
sort(p,p+n,cmp);
//排序
for(int i=1;i<=k;i++){
printf("Case #%d:\n",i);
int cnt,l,r;
cin>>cnt>>l>>r;
bool flag=true;
//输出前cnt名,flag如果更新了,就说明不是空的
for(int t=0;t<n&&cnt;t++){
if(p[t].age>=l&&p[t].age<=r){
flag=false;
printf("%s %d %d\n",p[t].name.c_str(),p[t].age,p[t].networth);
cnt--;
}
}
if(flag) printf("None\n");
}
return 0;
}
P1056
题目链接:题目详情 - 1056 Mice and Rice (pintia.cn)
题目大意:给定比赛人数和一组的最大人数,每组的第一名晋级,奇遇则标定分数,要求输出他们的排名(每组都要到最大人数,如果最后一组人数不够一样计算)
思路:写一个当前组,和一个晋级组,当前组每能np名比一次,找到最大重量的下标,赋值为-1,加入晋级组,遍历完一遍后,找到没有遍历过的,即为0的,赋值为当前晋级的人数+1,这里记得每次循环要把晋级的人赋值为0,不然他们都按上一次赋值为-1一直晋级。如果晋级组人数为1,完成,这里要特判一下组数为1的情况,会死循环,直接全输出1即可,最后一个人是不会更新到的,所以输出的时候如果当前人的排名为-1,那么他就是第一名
实现代码:
#include<bits/stdc++.h>
using namespace std;
int np,ng;//游戏人数,组的最大人数
int score[1010];//每个人的分数
vector<int> now,ne;//当前组,和晋级下一轮的序号
int ans[1010];//最终排名
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>np>>ng;
for(int i=0;i<np;i++) cin>>score[i];
for(int i=0;i<np;i++){
int x;
cin>>x;
now.push_back(x);
}
//初始化输入操作
if(ng==1){
for(int i=0;i<np;i++){
if(i!=0) cout<<" ";
cout<<"1";
}
return 0;
}
//特判一下数组大小为1的情况,避免死循环
while(now.size()!=1){
for(auto t: now){
ans[t]=0;
}
//更新
int len=now.size();
for(int i=0;i<len;i+=ng){
int mx=0,cur=0;
for(int j=i,cnt=0;j<len&&cnt<ng;j++,cnt++){
if(score[now[j]]>mx){
mx=score[now[j]];
cur=now[j];
}
}
ne.push_back(cur);
ans[cur]=-1;
}
//复制晋级组,并且清空晋级组
now=ne;
ne.clear();
//排名更新
for(int i=0;i<np;i++){
if(ans[i]==0){
ans[i]=now.size()+1;
}
}
}
for(int i=0;i<np;i++){
if(i!=0) cout<<" ";
//为-1就是第一名
if(ans[i]==-1){
cout<<"1";
}else{
cout<<ans[i];
}
}
return 0;
}
P1057
题目链接:题目详情 - 1057 Stack (pintia.cn)
题目大意:给定一个栈,有三种操作,push加入栈,pop删除最后一个元素并且输出,PeekMedian,输出这个栈的中位数
思路:这道题的考点就是最后这个中位数,我们可以用map数组来做,比如一个栈有8个元素,中位数指向的是第5个元素,那么他前面有4个元素,比如中位数是x,那么他前面的除x外的元素个数加上当前的x的元素个数就会大于栈的中的元素的一半。
我们以这个思路就可以维护这个数组的中间值,在加入和删除的操作的时候更新
实现代码:
#include<bits/stdc++.h>
using namespace std;
map<int,int> mp;//顺序存放,并且获得当前元素数量
map<int,int>::iterator mid;
int beforemid;
stack<int> sk;
void adjust(){
if(beforemid>=(sk.size()+1)/2){
mid--;//如果他前面的数大于等于了当前数的一半
beforemid-=mid->second;
//先移到下一个,在减去下一个的数量
}else if(beforemid+mid->second<(sk.size()+1)/2){
beforemid+=mid->second;
//先加上当前的数量,再移到下一个
mid++;
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
while(n--){
string op;
int x;
cin>>op;
if(op=="Pop"){
if(!sk.size()){
cout<<"Invalid"<<endl;
continue;
}
x=sk.top();//获得栈顶元素
sk.pop();
cout<<x<<endl;
mp[x]--;
if(x<mid->first) beforemid--;
adjust();
if(!mp[x]){
//如果删除这个元素之后map中没有这个元素了
//就把他删掉,避免后续更新移到这个元素
mp.erase(x);
}
}else if(op=="Push"){
//加入操作
cin>>x;
sk.push(x);
mp[x]++;
if(sk.size()==1){
//如果mid没有初始化
mid=mp.begin();
}else{
if(x<mid->first) beforemid++;
//如果x小于mid维护的值,mid前面的数就要+1
adjust();//再判段mid是否要移动
}
}else{
if(!sk.size()){
cout<<"Invalid"<<endl;
}else{
cout<<mid->first<<endl;
}
}
}
return 0;
}
P1058
题目链接:题目详情 - 1058 A+B in Hogwarts (pintia.cn)
题目大意:读入三个数,总共两组数据,相加,然后按题意进制即可
思路:略
实现代码:
#include<bits/stdc++.h>
using namespace std;
int galleon,sickle,knut;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int x,y,z;
char c;
cin>>x>>c>>y>>c>>z;
galleon+=x;
sickle+=y;
knut+=z;
cin>>x>>c>>y>>c>>z;
galleon+=x;
sickle+=y;
knut+=z;
// 计算
sickle+=knut/29;
knut%=29;
galleon+=sickle/17;
sickle%=17;
cout<<galleon<<"."<<sickle<<"."<<knut;
return 0;
}
P1059
题目链接:题目详情 - 1059 Prime Factors (pintia.cn)
题目大意:就是找到一个可以分解的所有质数,按要求输出
思路:从2找,如果可以整除加入数组,最后判段一下是否有剩余,有剩余说明剩下的一个数也是质数,加入进去,最后输出的时候特判一下,一开始就是质数的情况,用flag来判段,随后特判一下1的情况,我这里写的比较粗糙,但是效果还是对的
实现代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
map<LL,int> mp;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
LL num;
cin>>num;
cout<<num<<"=";
//输入操作
if(num==1){
cout<<"1";
return 0;
}//特判一下1的情况
bool flag=true;//判段是否被整除过,没有就是质数
for(LL i=2;i<=num/i;i++){
while(num%i==0){
flag=false;
mp[i]++;
num/=i;
}//直到把这个数除尽
}
//遍历map
for(auto t: mp){
if(t!=*mp.begin()){
cout<<"*";
}
cout<<t.first;
if(t.second!=1){
cout<<"^"<<t.second;
}
}
//判段最后剩余的质数
if(num&&num!=1){
if(flag){
cout<<num;
}else{
cout<<"*"<<num;
}
}
return 0;
}
P1060
题目链接:题目详情 - 1060 Are They Equal (pintia.cn)
题目大意:就是把一个数以科学技术法表示,表示的位数用给定一个n位来表示
思路:这里需要写一个函数将输入的数转换成科学技术法
第一步,找到小数点前有几位,即这个数的科学计数法的指数,然后把小数点删掉,让他变成一个完全的数字的数,随后删掉前导0,每次删除,指数都要减1,例如0.0001这种,删除前导0,指数变为负数,最后特判一下传入的数为0的情况,如果传入的数为0,前导0的数量是1个,这里就不能删除了,最后将这个数转换成科学计数法,如果这个数转换成的位数不足n,则需要补0,最后判段一下就就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n;
string to_sci(string s){
int pos=0;//记录小数点前有多少位
while(pos<s.length()&&s[pos]!='.') pos++;
if(pos<s.size()) s.erase(pos,1);//如果有小数点直接删掉
for(int i=0;i<s.size();i++){
if(s[i]=='0'){
s.erase(i,1);
i--;
pos--;
}else{
break;
}
}//因为指数可能为负数,所以要不断删除前导0,还要改变指数大小
if(s.size()==0) pos=0;
string ans="0."+s.substr(0,n);
for(int i=ans.size();i<n+2;i++) ans+='0';//如果末尾数量不足,要补0
ans+="*10^"+to_string(pos);
return ans;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
string str1,str2;
cin>>n>>str1>>str2;
string s1=to_sci(str1);
string s2=to_sci(str2);
if(s1==s2){
cout<<"YES "<<s1<<endl;
}else{
cout<<"NO "<<s1<<" "<<s2<<endl;
}
return 0;
}
P1061
题目链接:题目详情 - 1061 Dating (pintia.cn)
题目大意:前两个字符串,找第一个相同的是星期,第二个相同的是小时(具体字符要求根据题目读)。第3和第4个字符串,找到第一个相同的,输出他是当前字符串的第几个就是分钟
思路:暴力做
实现代码:
#include<bits/stdc++.h>
using namespace std;
string week_day[7]={"MON","TUE","WED","THU","FRI","SAT","SUN"};
int main(){
string s1,s2;
cin>>s1>>s2;
int cur;
//第一次相同
for(cur=0;;cur++){
if(s1[cur]==s2[cur]&&s1[cur]>='A'&&s1[cur]<='G'){
cout<<week_day[s1[cur]-'A']<<" ";
break;
}
}
//第二次相同
for(cur=cur+1;;cur++){
if(s1[cur]==s2[cur]&&(isdigit(s1[cur])||s1[cur]>='A'&&s1[cur]<='N')){
if(isdigit(s1[cur])){
printf("%02d:",s1[cur]-'0');
}else{
printf("%02d:",s1[cur]-'A'+10);
}
break;
}
}
cin>>s1>>s2;
//第三次相同
for(int i=0;;i++){
if(isalpha(s1[i])&&s1[i]==s2[i]){
printf("%02d",i);
break;
}
}
return 0;
}
P1062
题目链接:题目详情 - 1062 Talent and Virtue (pintia.cn)
题目大意:美德和才能都高于H的是圣人,美德低于H的是君主,都低于H,但是美德大于等于才能的是傻人,美德小于才能的是小人,都低于L的不计入。排序方式为按照总分数排序,按照美德分数排序,按照id升序排序
思路:如题实现即可
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n,l,h;//总人数,低线,高线
struct man{
string id;
int virtue,talent;
};
vector<man> sage,nobleman,foolman,smallman;
bool cmp(man A,man B){
if(A.virtue+A.talent!=B.virtue+B.talent) return A.virtue+A.talent>B.virtue+B.talent;
if(A.virtue!=B.virtue) return A.virtue>B.virtue;
return A.id<B.id;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>l>>h;
while(n--){
string id;
int vir,tal;
cin>>id>>vir>>tal;
man m;
m.id=id;
m.virtue=vir;
m.talent=tal;
//按照要求加入相应容器
if(vir>=h&&tal>=h){
sage.push_back(m);
}else if(vir>=h&&(tal<h&&tal>=l)){
nobleman.push_back(m);
}else if(vir>=l&&tal>=l&&vir>=tal){
foolman.push_back(m);
}else if(vir>=l&&tal>=l&&vir<tal){
smallman.push_back(m);
}
}
//输出总数,并且排序
cout<<sage.size()+nobleman.size()+foolman.size()+smallman.size()<<endl;
sort(sage.begin(),sage.end(),cmp);
sort(nobleman.begin(),nobleman.end(),cmp);
sort(foolman.begin(),foolman.end(),cmp);
sort(smallman.begin(),smallman.end(),cmp);
//按要求输出
for(auto t: sage){
cout<<t.id<<" "<<t.virtue<<" "<<t.talent<<endl;
}
for(auto t:nobleman){
cout<<t.id<<" "<<t.virtue<<" "<<t.talent<<endl;
}
for(auto t:foolman){
cout<<t.id<<" "<<t.virtue<<" "<<t.talent<<endl;
}
for(auto t:smallman){
cout<<t.id<<" "<<t.virtue<<" "<<t.talent<<endl;
}
return 0;
}
P1063
题目链接:题目详情 - 1063 Set Similarity (pintia.cn)
题目大意:两个集合之间的相似度即相同元素/总元素种类个数,要求给定n个集合,k次询问,每次询问要求获得两个集合的相似度,精确到小数点后1位
思路:用unordered_set,储存,后面询问的时候只要遍历一遍看第一个集合有没有就可以了
实现代码:
#include<bits/stdc++.h>
using namespace std;
unordered_set<int> st[60];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++){
int cnt;
cin>>cnt;
while(cnt--){
int x;
cin>>x;
st[i].insert(x);
}
}
//储存操作
int k;
cin>>k;
while(k--){
int a,b;
cin>>a>>b;
//首先默认为第一个结合的的大小,这里第一个集合是set已经去重了
int all=st[a].size();
int common=0;
for(auto t: st[b]){
//然后看是不是相同元素,是就不加总数,加common,不是就加总数
if(st[a].count(t)){
common++;
}else{
all++;
}
}
printf("%.1lf%\n",common*100.0/all);
}
return 0;
}
P1064
题目链接:题目详情 - 1064 Complete Binary Search Tree (pintia.cn)
题目大意:要求按照镜像树构建完全二叉树,输出构建后的树的层序遍历
思路:首先我们想到了堆,x结点的左子树为2*x,右子树为2*x+1,按照这个下标实现一遍中序遍历,我们就可以得到的第i个结点该放什么
例如如果是10个结点,按照堆的中序遍历获得的数组是
8 4 9 2 10 5 1 6 3 7
原数组排序后获得
0 1 2 3 4 5 6 7 8 9
按照这个顺序构建,数组的第8个数放0,第4个数放1,第9个数放2....
这样我们就获得了
6 3 8 1 5 7 9 0 2 4
这个数组就是他的层序遍历,因为堆的层序遍历也是1 2 3 4 5 6 7 8 9,只不过将堆的顺序改了,对应到当前数组,从小到大的方式构建,完成
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n;//结点总数
int a[1010];//原数组
vector<int> inordered;//按照堆排序获得的下标
int ans[1010];
void inordered_traverse(int x){
//中序遍历
if(x>n) return ;
inordered_traverse(2*x);
inordered.push_back(x);
inordered_traverse(2*x+1);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
//中序遍历获得下标
inordered_traverse(1);
for(int i=0;i<n;i++){
cin>>a[i];
}
//排序
sort(a,a+n);
//按照对应位置赋值
for(int i=0;i<n;i++){
ans[inordered[i]]=a[i];
}
for(int i=1;i<=n;i++){
if(i!=1) cout<<" ";
cout<<ans[i];
}
return 0;
}
P1065
题目链接:题目详情 - 1065 A+B and C (64bit) (pintia.cn)
题目大意:T组询问,每次询问A+B是否大于C
思路:直接上java,python
实现代码:
import java.io.*;
import java.math.BigInteger;
public class Main {
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
public static void main(String[] args) throws IOException {
int T=Integer.parseInt(bf.readLine());
for(int i=1;i<=T;i++) {
pw.printf("Case #%d: ",i);
String[] temp=bf.readLine().split(" ");
BigInteger A=new BigInteger(temp[0]);
BigInteger B=new BigInteger(temp[1]);
BigInteger C=new BigInteger(temp[2]);
if(A.add(B).compareTo(C)>0) {
pw.println("true");
}else {
pw.println("false");
}
}
pw.flush();
}
}
P1066
题目链接:题目详情 - 1066 Root of AVL Tree (pintia.cn)
题目大意:构建一个平衡数,输出根节点
思路:如果一个数大于当前结点,我们就把他放到这个结点的右边,直到他能够成为叶子结点,反之小于也是。插入过后我们需要分析插入操作对平衡树的影响,来调整结构,共分为4种情况
1)左左
即,结点左边的高度=右边的高度+2,且结点的左节点的左高度大于右高度
这里假设88为a结点,70为b结点,我们需要将b的右结点给a的左节点,同时将高度一起给过去,再把a设为b的右结点,此时b的右高度++
2)右右
即,结点右边的高度=左边的高度+2,且结点的右节点的右高度大于左高度
相似与第一种情况,将96结点的左子树给88结点的右边,同时赋予高度,再将96结点的左子树设为88结点 ,次数96结点的左高度++
3)左右
左右和右左情况相对麻烦,需要 65结点的左子树分给61的右节点,右子树分给70的左节点,再然后将65的左节点设为61,右节点设为70,左高度为61的高度+1,右高度为70的高度+1
4)右左
类似于左右,此时70作为根节点,96的左高度大于右高度,处于右左情况,将88的左子树分给70的右子树,再将88的右子树分给96当左子树,最后88的右子树改为96,左子树改为70,左高度为70结点高度+1,右高度为96结点高度+1
分析:注意将子树赋予的时候要将高度同时赋给过去,最后分析一遍所有结点高度变化,一般都是根节点除遗漏变化需要补上,还有就是改变一个结点的左右子树,发现他的左右子树空缺,需要移至最上结点的左右子树赋予,不会让一个结点突然消失左右子树
实现代码:
#include<bits/stdc++.h>
using namespace std;
struct node{
int data,l_height,r_height;
node *l,*r;
};
void insert(node *&root,int x){
//如果没有初始化,要先初始化构建一个树的根节点
if(root==NULL){
root=new node;
root->data=x;
root->l_height=root->r_height=0;
root->l=root->r=NULL;
}else{
//插入一个结点
if(x>root->data){
insert(root->r,x);
root->r_height=max(root->r->l_height,root->r->r_height)+1;
}else{
insert(root->l,x);
root->l_height=max(root->l->l_height,root->l->r_height)+1;
}
//根据4种情况改变树的结构
if(root->l_height-root->r_height==2){
//如果左子树的高度大于右子树的高度2
if(root->l->l_height>root->l->r_height){
//左左
node *a=root;
node *b=root->l;
root=b;
a->l=b->r;
a->l_height=b->r_height;
b->r=a;
b->r_height++;
}else{
//左右
node*a=root;
node*b=root->l;
node*c=root->l->r;
root=c;
b->r=c->l;
b->r_height=c->l_height;
a->l=c->r;
a->l_height=c->r_height;
c->l=b;
c->r=a;
c->l_height=b->l_height+1;
c->r_height=a->r_height+1;
}
}else if(root->l_height-root->r_height==-2){
//如果右子树的高度大于左子树的高度2
if(root->r->l_height<root->r->r_height){
//右右
node *a=root;
node *b=root->r;
root=b;
a->r=b->l;
a->r_height=b->l_height;
b->l=a;
b->l_height++;
}else{
//右左
node*a=root;
node*b=root->r;
node*c=root->r->l;
root=c;
a->r=c->l;
a->r_height=c->l_height;
b->l=c->r;
b->l_height=c->r_height;
c->l=a;
c->r=b;
c->l_height=a->l_height+1;
c->r_height=b->r_height+1;
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin>>n;
node* Tree=NULL;
while(n--){
int x;
cin>>x;
insert(Tree,x);
}
cout<<Tree->data;
return 0;
}
P1067
题目链接:题目详情 - 1067 Sort with Swap(0, i) (pintia.cn)
题目大意:通过将0和其他数调换位置,找到最小调换次数是的数组为升序
思路:我们模拟一遍题目给的样例可以发现,如果0到了本身的位置,还要往后走,那就会出现无0换,需要的步数要加1,而像题目中给的5个数的例子,0最后到达了原位,则是只有1个有0环,步数-1
随后我们分析,从0点往后dfs,第一个走的一圈必然是包含0的,因为从0点开始往回成换最后一个一定是0,如果还能往后走,说明有非0环,需要步数+1,因为要把0拿开,还要再把0换回来
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,a[N];
bool st[N];//判段这个点是否走过
int cycle_length;
void dfs(int x){
if(st[x]) return;
st[x]=true;
cycle_length++;
dfs(a[x]);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
int total=0;
for(int i=0;i<n;i++){
if(!st[i]){
if(i!=a[i]){
cycle_length=0;
dfs(i);
if(i==0) total+=cycle_length-1;//含0环
else total+=cycle_length+1;//不含0环
}
}
}
cout<<total;
return 0;
}
P1068
题目链接:题目详情 - 1068 Find More Coins (pintia.cn)
题目大意:给定n个数,要求他们的和为m,输出最小情况(类似于字符串的比较,如果第i位相同比i+1位)
思路:这道题当然是可以用背包问题去做的,但是我这里用的是暴搜,搜索每个数的往后加的各种情况,如果这种情况大于m就剪枝,直到第一次出现等于的时候,那么就是最小情况。这里如果不优化,最后一个点可能过不了
优化方案:比如 1 2 3 4 4 4 4 4 4 4 4 4 5,如果1 3 4这种方法因为各种原因不能满足条件,往后走就还是4,还是1 3 4的情况,所以会出现很多重复,这里如果不满足条件,且元素重复,则跳过,避免重复dfs
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;//面值数量和需要的总数
int a[10010];
vector<int> path;
bool flag=true;//如果没有找到就是true
void dfs(int x,int sum){
//剪枝,找到过了,或者和大于了
if(!flag||sum>m) return;
path.push_back(x);
if(sum==m&&flag){
//找到了按要求输出
flag=false;
for(int i=0;i<path.size();i++){
if(i!=0) cout<<" ";
cout<<a[path[i]];
}
cout<<"\n";
}
//没找到就往后遍历各种情况
for(int i=x+1;i<n&&flag;){
dfs(i,sum+a[i]);
if(flag){
int j;
//去重
for(j=i+1;j<n&&a[i]==a[j];j++);
i=j;
}
}
//回溯
path.pop_back();
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
for(int i=0;i<n&&flag;){
dfs(i,a[i]);
if(flag){
//去重
int j;
for(j=i+1;j<n&&a[i]==a[j];j++);
i=j;
}
}
if(flag){
cout<<"No Solution";
}
return 0;
}
P1069
题目链接:题目详情 - 1069 The Black Hole of Numbers (pintia.cn)
题目大意:经典数字黑洞,一个4位数从大到小排减去从小到大排最后会得到6174,除非两个相等
思路:按题意做,但是要特判一下6174,否则进不了循环
实现代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int num;
cin>>num;
if(num==6174){
printf("7641 - 1467 = 6174\n");
return 0;
}
for(int i=1;num!=6174&&num!=0;i++){
int temp[4];
temp[0]=num/1000;
temp[1]=num%1000/100;
temp[2]=num%100/10;
temp[3]=num%10;
sort(temp,temp+4,greater<int>());
int big=0;//从大到小排序
for(int i=0,t=1000;i<4;i++,t/=10){
big+=temp[i]*t;
}
int small=0;//从小到大排序
sort(temp,temp+4);
for(int i=0,t=1000;i<4;i++,t/=10){
small+=temp[i]*t;
}
printf("%04d - %04d = %04d\n",big,small,big-small);
num=big-small;
}
return 0;
}
P1070
题目链接:题目详情 - 1070 Mooncake (pintia.cn)
题目大意:给定月饼的种类数量和最大需求,每件月饼给出总体积和总体积下的收益。要求获得最大需求下最大的利润
思路:就是按性价比排个序,随后如果遍历后面的物品,如果能全拿,就全拿,体积减掉,如果不能全拿,就能拿多少拿多少
实现代码:
#include<bits/stdc++.h>
using namespace std;
int n;
double max_size;//月饼种类和最大体积
struct mooncake{
double v,price;
double xingjiabi;
}m[1010];
bool cmp(mooncake A,mooncake B){
return A.xingjiabi>B.xingjiabi;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>max_size;
for(int i=0;i<n;i++){
cin>>m[i].v;
}
for(int i=0;i<n;i++){
cin>>m[i].price;
m[i].xingjiabi=m[i].price/m[i].v;
}
//输入操作
sort(m,m+n,cmp);
double profit=0;
for(int i=0;i<n;i++){
//如果大于0,可以全拿,==0可以放下面一起break
if(max_size-m[i].v>0){
max_size-=m[i].v;
profit+=m[i].price;
}else{
//能拿多少拿多少
profit+=m[i].price*max_size/m[i].v;
break;
}
}
printf("%.2lf",profit);
return 0;
}
后记:这篇文章只记录了PAT甲级组前70题的题解,本想一篇文章写完的,但是写到后面会太卡了,在后续章节,我会持续跟进。如有对给的题解中有更好的方法,欢迎指出讨论