PAT甲级题解1001-1030

PAT甲级题解1001-1030

1001

样例

输入:

-1000000 9

输出

-999,991

思路和坑点

输出结果不是很长,所以把sum从低到高每一位直接拿出来存在字符串中,每三位加入一个逗号,当sum取完所有位时结束循环,负号最后处理。然后倒着输出结果。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    string ans;
    int a,b;
    scanf("%d%d",&a,&b);
    a+=b;
    int sum=a,cnt=0;
    while(1){
        ans.push_back('0'+abs(sum%10));
        sum/=10;
        if(sum==0)    break;
        cnt++;
        if(cnt%3==0)
            ans.push_back(',');
    }
    if(a<0)
        ans.push_back('-');
    for(int i=ans.size()-1;i>=0;i--)
        putchar(ans[i]);
    return 0;
} 

1002

样例

输入:

2 1 2.4 0 3.2
2 2 1.5 1 0.5

输出:

3 2 1.5 1 2.9 0 3.2

思路和坑点

使用map容器存放每一项的指数e和系数c对应关系,合并时候直接在map上操作,然后map逆序遍历,按照指数由大到小输出结果。
  坑点
  处理map时候,如果遇到系数为零的直接剔除,此题的浮点数比较不严格直接可以使用==进行判定。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    int a,b;
    map<int,double> mp;
    cin>>a;
    for(int i=0;i<a;i++){
        int e;
        double c;
        cin>>e>>c;
        mp[e]=c;
    }
    cin>>b;
    for(int i=0;i<b;i++){
        int e;
        double c;
        cin>>e>>c;
        mp[e]+=c;
        if(mp[e]==0)            //如果系数相加后为0 ,则剔除这一项 
            mp.erase(e);
    }
    cout<<mp.size();
    for(auto it=mp.rbegin();it!=mp.rend();it++){
        printf(" %d %.1f",it->first,it->second);
    }
    return 0;
} 

## 1003

样例

输入:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

输出:

2 4

思路和坑点

直接使用Dijkstra+dfs计算路径,没得到一条路径就进行统计,然后根据第二尺度进行优化。其实一遍Dijkstra算法应该可以,但是判定逻辑可能不是很好理清楚。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 505
#define INF 0x3fffffff
int G[MAXN][MAXN];
int weight[MAXN]={0};
bool vis[MAXN]={false};
int dis[MAXN];
vector<int> pre[MAXN];
int st,ed,n,m,ansnum=0,answ=0;
vector<int> temppath;
void Dijkstra(){
    fill(dis,dis+MAXN,INF);
    dis[st]=0;
    for(int i=0;i<n;i++){
        int u=-1,MIN=INF;
        for(int j=0;j<n;j++){
            if(vis[j]==false&&dis[j]<MIN){
                u=j;
                MIN=dis[j];
            }
        }
        if(u==-1) return;
        vis[u]=true;
        for(int v=0;v<n;v++){
            if(vis[v]==false&&G[u][v]!=INF){
                if(dis[u]+G[u][v]<dis[v]){
                    dis[v]=dis[u]+G[u][v];
                    pre[v].clear();
                    pre[v].push_back(u);
                }
                else if(dis[u]+G[u][v]==dis[v]){
                    pre[v].push_back(u);
                }
            }
        }
    }

}
void dfs(int s){
    if(s==st){
        ansnum++;
        temppath.push_back(s);
        int val=0;
        for(int i=0;i<temppath.size();i++){
            val+=weight[temppath[i]];
        }
        answ=max(answ,val);
        temppath.pop_back();
        return;
    }
    temppath.push_back(s);
    for(int i=0;i<pre[s].size();i++){
        dfs(pre[s][i]);
    }
    temppath.pop_back();
}
int main(void){
    fill(G[0],G[0]+MAXN*MAXN,INF);
    cin>>n>>m>>st>>ed;
    for(int i=0;i<n;i++){
        scanf("%d",&weight[i]);
    }
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        G[a][b]=G[b][a]=c;
    }
    Dijkstra();
    dfs(ed);
    cout<<ansnum<<' '<<answ;
    return 0;
} 

1004

样例

输入:

2 1
01 1 02

输出:

0 1

思路和坑点

多叉树的使用二维变长数组存储,使用层次遍历,统计每层没有孩子的结点,然后输出最终的统计结果。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100
vector<int> f[MAXN];
int laycnt[MAXN]={0};
int main(void){
    int n,m;
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int id,k;
        scanf("%d%d",&id,&k);
        for(int j=0;j<k;j++){
            int temp;
            scanf("%d",&temp);
            f[id].push_back(temp);
        }
    }
    queue<int> q;
    int layer=0,last=1,tail;
    q.push(1);
    while(!q.empty()){
        int now=q.front();
        q.pop();
        if(f[now].size()==0)
            laycnt[layer]++;
        else{
            int i;
            for(i=0;i<f[now].size();i++){
                q.push(f[now][i]);
            }
            tail=f[now][i-1];
        }
        if(now==last){
            layer++;
            last=tail;
        }
    }
    for(int i=0;i<layer;i++){
        if(i) putchar(' ');
        printf("%d",laycnt[i]);
    }
    return 0;
} 

1005

样例

输入:

12345

输出:

one five

思路和坑点

由于题目的数字不超过100位,因此每位的和最大不会超过900,因此对和分三段进行判定输出。(英文单词不要拼错)

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    string shu[10]={"zero","one","two","three","four","five","six","seven","eight","nine"};
    string str;
    int ans=0;
    cin>>str;
    for(int i=0;i<str.size();i++){
        ans+=str[i]-'0';
    }
    if(ans>=100)
        cout<<shu[ans/100]<<' '<<shu[ans/10%10]<<' '<<shu[ans%10];
    else if(ans>=10)
        cout<<shu[ans/10]<<' '<<shu[ans%10];
    else
        cout<<shu[ans];
    return 0;
} 

1006

样例

输入:

3
CS301111 15:30:28 17:00:10
SC3021234 08:00:00 11:25:25
CS301133 21:45:00 21:58:40

输出:

SC3021234 CS301133

思路和坑点

由于没有同时登入和登出的情况,所以直接排序就行了,使用一个全局变量控制cmp函数实现两种不同的排序规则。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef struct node{
    string id,in_t,out_t;
}Node;
int flag=1;
bool cmp(Node a,Node b){
    if(flag==1)
        return a.in_t<b.in_t;
    else return a.out_t>b.out_t;
}
int main(void){
    int n;
    cin>>n;
    vector<Node> v(n);
    for(int i=0;i<n;i++){
        cin>>v[i].id>>v[i].in_t>>v[i].out_t;
    }
    sort(v.begin(),v.end(),cmp);
    cout<<v[0].id<<' ';
    flag=0;
    sort(v.begin(),v.end(),cmp);
    cout<<v[0].id;
    return 0;
} 

1007

样例

输入:

10
-10 1 2 3 4 -5 -23 3 7 -21

输出:

10 1 4

思路和坑点

典型的动态规划题目,也可使直接使用在线处理的算法。在线处理可以去看陈越姥姥慕课上数据结构的课程讲解,动态规划可以参见该博客的动态规划总结的那篇文章。
  坑点
  注意特判就行了,当全部是负数的时候,输出0,以及序列的第一个和最后一个值。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    int n,dp,tdp,st,ed,_st,_ed;         //tdp _st _ed临时存放最优子问题,dp st ed存放最后结果 
    int first,last;                     //记录特判的情况 
    scanf("%d",&n);
    dp=tdp=-0x3fffffff;                 //初始化 
    for(int i=0;i<n;i++){
        int temp;
        scanf("%d",&temp);
        if(i==0) first=temp;
        if(i==n-1) last=temp;
        //动态规划转移方程 dp[i]=max(A[i],dp[i-1]+A[i])
        if(temp>tdp+temp){              //如果当前构成一个单独的最优子序列,该序列只有1个元素 
            tdp=temp;
            _st=temp;_ed=temp; 
        }
        else{                           //如果当前位加入前一位的最优子序列,更新临时的tdp,结尾为当前位 
            tdp+=temp;
            _ed=temp;
        }
        if(tdp>dp){                     //筛选每一个子问题,找到最优解 
            dp=tdp;
            st=_st;ed=_ed;
        }    
    }
    if(dp<0){                           //特判 
        dp=0;st=first;ed=last;
    } 
    printf("%d %d %d",dp,st,ed);
    return 0;
} 

1008

样例

输入:

3 2 3 1

输出:

41

思路和坑点

简单模拟,直接按照规则进行处理计算便可,等待时间可以直接计算,因为每一层都需要停留,所以直接在读入n的时候计算等待部分的时间。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define ut 6                    //时间常量
#define dt 4
#define st 5
int main(void){
    int n,ans=0,pre=0;
    scanf("%d",&n);
    ans+=n*st;                  //直接计算等待时间
    for(int i=0;i<n;i++){
        int temp;
        scanf("%d",&temp);
        int k=temp-pre;
        if(k>0)                 //模拟过程
            ans+=ut*k;
        else if(k<0)
            ans+=dt*(-k);
        pre=temp;
    }
    cout<<ans;
    return 0;
} 

1009

样例

输入:

2 1 2.4 0 3.2
2 2 1.5 1 0.5

输出:

3 3 3.6 2 6.0 1 1.6

思路和坑点

使用map<int,double>记录多项式,存放第一个,第二个式子边读入边处理,直接讲读入的每一项乘上第一个多项式,然后合并同类项到输出结果中。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    map<int,double> a,ans;
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){                       //输入,使用a存放第一个式子 
        int e;
        double c;
        scanf("%d%lf",&e,&c);
        a[e]=c;
    }
    scanf("%d",&n);                             //读入第二个式子,每次读一项,边读入边处理 
    for(int i=0;i<n;i++){
        int e;
        double c;
        scanf("%d%lf",&e,&c);
        for(auto it=a.begin();it!=a.end();it++){    //把当前项乘入式子a,即指数增加e,系数乘以c,然后并入ans中 
            ans[it->first+e]+=it->second*c;
            if(ans[it->first+e]==0)                 //系数为0时,剔除 
                ans.erase(it->first+e);
        }
    }
    printf("%d",ans.size());                        //输出结果 
    for(auto it=ans.rbegin();it!=ans.rend();it++){
        printf(" %d %.1f",it->first,it->second);
    }
    return 0;
} 

1010

样例

样例1

输入:

6 110 1 10

输出:

2
样例2

输入:

1 ab 1 2

输出:

Impossible
补充测试点

输入:

12 c 1 10

输出:

13

思路和坑点

此题的难点在于确定另外一个数进制的上下界,对于一个数字来说其进制的下届为单个位上的数字最大值+1,比如178900,该数的进制最小是10;其上界为该数的十进制数值(取不到),比如c9,该数的十进制129,如果其进制等于129,那么该数字应该表示成1,如果超过129,那么该数字无法用整数表示。
  基于以上的原理,在该进制区间内筛选,由于范围比较大,因此使用long long数据类型,并且考虑溢出,使用二分法查找。
  坑点
  如果出现了该数的进制的下届超过了其十进制数值情况,上界应该下届和十进制数值中的最大值,如补充测试点所示。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int tonum(char c){                          //转化为数字 
    return isdigit(c)?c-'0':c-'a'+10;
}
ll todec(string s,ll r){                    //转化为十进制数 
    ll sum=0L;
    for(int i=0;i<s.size();i++){
        sum=sum*r+tonum(s[i]);
    }
    return sum;
}
int main(void){
    string n1,n2;
    int tag;
    ll radix;
    cin>>n1>>n2>>tag>>radix;
    if(n1=="0"||n2=="0"){                    //特判 0的情况 
        if(n1==n2)    putchar('2');
        else puts("Impossible");
        return 0;
    }
    if(tag==2) swap(n1,n2);                 //统一第一个数为已知 
    
    ll a=todec(n1,radix);                   //计算n1的十进制 
    ll low=-1,high;                         //n2的radix的上下界                         
    for(int i=0;i<n2.size();i++){
        low=max((int)low,tonum(n2[i])+1);   //下界为每一位+1的最大值; 
    }
    high=a>low?a:low;                       //上界n1十进制数和者其进制下界的最大值
    ll ans=0L;
    while(low<=high){                       //二分查找最小的可能的radix 
        ll mid=(low+high)/2;
        if(todec(n2,mid)>a||todec(n2,mid)<0)    //如果进制太大,包括溢出成为负数的情况 
            high=mid-1;
        else if(todec(n2,mid)<a)                //如果进制太小 
            low=mid+1;
        else{
            ans=mid;break;
        }
    } 
    if(ans==0)    puts("Impossible");
    else    cout<<ans;     
    return 0;
} 

1011

样例

输入:

1.1 2.5 1.7
1.2 3.1 1.6
4.1 1.2 1.1

输出:

T T W 39.31

思路和坑点

取每场比赛的最大的点,并记录每场的输赢标记,按照公式计算就可以了。

AC代码

#include<bits/stdc++.h>
using namespace std;
void getans(char &c,double &p){
    double n2,n3;
    cin>>p>>n2>>n3;
    c='W';
    if(n2>p){
        c='T';p=n2;
    }
    if(n3>p){
        c='L';p=n3;
    }
}
int main(void){
    double ans=1,p;
    char c;
    for(int i=0;i<3;i++){
        getans(c,p);
        ans*=p;
        if(i) putchar(' ');
        putchar(c);
    }
    printf(" %.2f",(ans*0.65-1)*2);
    return 0;
} 

1012

样例

输入:

5 6
310101 98 85 88
310102 70 95 88
310103 82 87 94
310104 91 91 91
310105 85 90 90
310101
310102
310103
310104
310105
999999

输出:

1 C
1 M
1 E
1 A
3 A
N/A

思路和坑点

对各个成绩进行排名,并且按照怕排名更新rank。同时使用map标记是否有记录存在,由于学生结构体的数据量比较大,所以采用排下标的方式,节省时间。另外,由于成绩只有三科,所以平均分的比较直接算成总分比较,可以有效避免平均以后浮点数比较的坑。
  结构体下标排序参见我的博客分类PAT中的《PAT总结》中结构体部分的说明。排序的比较函数写成一个,使用全局变量标记控制排序规则。
  坑点
  PAT中的所有的排名方式大多是按照一下规则,并列时同排名,不并列时,排名是实际的次序,而不是紧跟并列的排名。比如:1 1 2 3 4,有前两个并列的情况,PAT中普遍采用的方法是1 1 3 4 5.

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef struct node{                //学生结构体 
    int id,c,m,e,a;                 //成绩 
    int ar,cr,mr,er;                //排名 
}Node;
vector<Node> ST;
map<int,int> mp;                    //记录是否存在该记录 
vector<int> sidx;                   //结构体数据量较大,使用下标排序,减少时间 
char tag;    
bool cmp(int i,int j){              //比较函数,使用全局标记控制 
    switch(tag){
        case 'A' :return ST[i].a>ST[j].a;break;
        case 'C' :return ST[i].c>ST[j].c;break;
        case 'M' :return ST[i].m>ST[j].m;break;
        case 'E' :return ST[i].e>ST[j].e;break;
    }
}
void check(){
    int id;
    scanf("%d",&id);                                //输出结果 
    if(mp.find(id)==mp.end())    puts("N/A");
    else{
        int v=mp[id];
        int rank=ST[v].er; char ch='E';            //根据优先级,由低到高进行比较,节省比较次数 
        if(ST[v].mr<=rank)        {rank=ST[v].mr;ch='M';} 
        if(ST[v].cr<=rank)        {rank=ST[v].cr;ch='C';} 
        if(ST[v].ar<=rank)        {rank=ST[v].ar;ch='A';} 
        printf("%d %c\n",rank,ch);
    }
}
int main(void){
    int n,k;
    scanf("%d%d",&n,&k);
    ST.resize(n);    sidx.resize(n);
    for(int i=0;i<n;i++){
        scanf("%d%d%d%d",&ST[i].id,&ST[i].c,&ST[i].m,&ST[i].e);
        mp[ST[i].id]=i;    sidx[i]=i;
        ST[i].a=ST[i].c+ST[i].m+ST[i].e;
    }
    tag='A';
    sort(sidx.begin(),sidx.end(),cmp);              //排序 
    int pre=sidx[0];    ST[pre].ar=1;               //初始化第一个排名 
    for(int i=1;i<n;i++){
        int v=sidx[i];                              //后序排名由上一个决定,分数相同,排名相同 
        if(ST[v].a==ST[pre].a)    ST[v].ar=ST[pre].ar;
        else    ST[v].ar=i+1;                       //如果不同,更新为排名 
        pre=v; 
    }
    tag='C';
    sort(sidx.begin(),sidx.end(),cmp);
    pre=sidx[0];    ST[pre].cr=1;
    for(int i=1;i<n;i++){
        int v=sidx[i];
        if(ST[v].c==ST[pre].c)    ST[v].cr=ST[pre].cr;
        else    ST[v].cr=i+1;
        pre=v;
    }
    tag='M';
    sort(sidx.begin(),sidx.end(),cmp);
    pre=sidx[0];    ST[pre].mr=1;
    for(int i=1;i<n;i++){
        int v=sidx[i];
        if(ST[v].m==ST[pre].m)    ST[v].mr=ST[pre].mr;
        else    ST[v].mr=i+1;
        pre=v;
    }
    tag='E';
    sort(sidx.begin(),sidx.end(),cmp);
    pre=sidx[0];    ST[pre].er=1;
    for(int i=1;i<n;i++){
        int v=sidx[i];
        if(ST[v].e==ST[pre].e)    ST[v].er=ST[pre].er;
        else    ST[v].er=i+1;
        pre=v;
    }
    for(int i=0;i<k;i++){
        check();
    }
    return 0;
} 

1013

样例

输入:

3 2 3
1 2
1 3
1 2 3

输出:

1
0
0

思路和坑点

使得一个图连通,则必须只能有一个连通分量,如果不连通,则添加最少的边使得其连通,最小的边数应该为连通分量数-1.将每次查询的被攻占的城市提前标记为已访问,然后遍历图,计算连通分量数,然后输出结果。dfs简单好写,但是如果dfs的递归层数过多的时候建议使用bfs。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1003
int n,m,k;
vector<int> G[MAXN];
bool vis[MAXN];
void dfs(int u){                        //dfs遍历 
    vis[u]=true;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(vis[v]==false)
            dfs(v);
    }
}
int dfsTrave(int x){                    //dfs驱动函数 
    fill(vis,vis+MAXN,false);           //初始化访问标记数组 
    vis[x]=true;                        //将已被攻占的城市提前标记为已访问 
    int cnt=0;
    for(int u=1;u<=n;u++){
        if(vis[u]==false){
            dfs(u);                     //dfs遍历每一个连通分量 
            cnt++;                      //连通分量数统计 
        }
    }
    return cnt;
}
int main(void){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
    for(int i=0;i<k;i++){               //多次查询并输出 
        int v;
        scanf("%d",&v);
        printf("%d\n",dfsTrave(v)-1);
    }
    return 0;
} 

1014

样例

输入:

2 2 7 5
1 2 6 4 3 534 2
3 4 5 6 7

输出:

08:07
08:06
08:10
17:00
Sorry

思路和坑点

简单模拟服务队列。使用客户结构体(记录业务花费时长和服务结束时间,其中服务结束时间初始化为-1,用于识别未被服务的人数)和队列结构体(队列当前业务的结束时间和服务的队列,当业务结束时,弹出队首元素,更新队列以及时间)。
  第一步先把已有的顾客装填入队列,并初始化第一位顾客的业务结束时间,然后开始时间计数,每个时刻,遍历所有的队列,如果队列中刚好业务结束,则更新队列,一旦有顾客出队,且等待区有人,则将等待区的顾客纳入队中。由于从小到大遍历,因此满足优先进入人数较少且编号较小的队列。
  使用队列标记flag统计空队列个数,如果都空则表示服务全部结束,提前结束循环。
  坑点:
  由于17:00(包含17:00在内)之后就停止接受顾客,因此该时间点为服务起始时间的上边界(17:00不会是起始时间,最晚起始时间为16:59)。另外,如果某个人在17点之前被服务,但是结束时间超过17点,仍然有效,只有那些17点之后还未被服务的人要输出sorry。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define st 8*60
#define ed 17*60
void prt(int x){                            //输出查询结果 
    if(x==-1) puts("Sorry");                //未被服务的情况 
    else printf("%02d:%02d\n",x/60,x%60);
}
typedef struct wind{                        //窗口结构体 
    int endtime=8*60;
    queue<int> q;
}WIN;
typedef struct cust{                        //顾客结构体 
    int cost;
    int endt=-1;
}CU;
int n,m,k,q;
vector<WIN> window;                         //窗口数组 
vector<CU> cus;                             //顾客数组 
int main(void){
    scanf("%d%d%d%d",&n,&m,&k,&q);
    window.resize(n);    cus.resize(k);
    for(int i=0;i<k;i++){                   //读入时间花费 
        scanf("%d",&cus[i].cost);
    }
    int cnt=0;
    for(cnt=0;cnt<n*m&&cnt<k;cnt++){        //分配顾客到队列,全部填满或者顾客全部分配完时结束循环 
        window[cnt%n].q.push(cnt);
    }
    for(int i=0;i<n;i++){                   //初始化分配后第一位顾客的服务时间 
        if(!window[i].q.empty()){
            int now=window[i].q.front();
            window[i].endtime=cus[now].endt=st+cus[now].cost;
        }
    } 
    for(int i=st;i<ed;i++){                 //时间从8点开始走到17点结束 
        int flag=0;                         //空队列计数器 
        for(int j=0;j<n;j++){
            if(!window[j].q.empty()){       //如果队列不空,则处理 
                if(window[j].endtime==i){   //如果当前业务刚好结束 ,弹出队首的顾客 
                    window[j].q.pop();             
                    if(cnt<k)        window[j].q.push(cnt++);    //如果等待区还有顾客,则入队 
                    // 与上一步顺序不能换,比如队列一次只接受一人,pop后为空,所以要不空时更新
                    if(!window[j].q.empty()){                    //如果此时队列仍然有人,更新队列的服务结束时间, 
                        int now=window[j].q.front();
                        window[j].endtime=cus[now].endt=i+cus[now].cost;
                    }    
                }    
            }
            if(window[j].q.empty()) flag++; //如果队列空,则统计
        } 
        if(flag==n)    break;               //如果队列全空,提前结束
    }
    for(int i=0;i<q;i++){
        int temp;
        scanf("%d",&temp);
        prt(cus[temp-1].endt);
    }
    return 0;
} 

1015

样例

输入:

73 10
23 2
23 10
-2

输出:

Yes
Yes
No

思路和坑点

典型的进制转换,但是多了一个reverse的过程,但是十进制转化成给定r进制的时候,每一位获取顺序是反着的,因此刚好可以和再转化为十进制的过程联合起来写在一起。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
bool prime[MAXN];
void getprime() {                           //筛选法,获取素数表 
    fill(prime,prime+MAXN,true);
    prime[0]=prime[1]=false;
    for(int i=2;i<MAXN;i++){
        if(prime[i]){
            for(int j=i+i;j<MAXN;j+=i)
                prime[j]=false;
        }
    }
}
int torever(int n,int r){                   //获得逆转数的十进制 
    int ret=0;
    do{                                     //按照转化为r进制数的方法,获取每一位,然后同时转化为对应的十进制 
        ret=ret*r+n%r;
        n/=r;
    }while(n!=0);
    return ret;
}
int main(void){
    getprime();
    while(1){
        int n,r;
        scanf("%d",&n);
        if(n>=0){                           //符合条件时进行判定,不符合输入条件时直接结束循环 
            scanf("%d",&r);
            if(prime[n]&&prime[torever(n,r)])
                puts("Yes");
            else
                puts("No");
        }
        else
            break;
    } 
    return 0;
} 

1016

样例

输入:

10 10 10 10 10 10 20 20 20 15 15 15 15 15 15 15 20 30 20 15 15 10 10 10
10
CYLL 01:01:06:01 on-line
CYLL 01:28:16:05 off-line
CYJJ 01:01:07:00 off-line
CYLL 01:01:08:03 off-line
CYJJ 01:01:05:59 on-line
aaa 01:01:01:03 on-line
aaa 01:02:00:01 on-line
CYLL 01:28:15:41 on-line
aaa 01:05:02:24 on-line
aaa 01:04:23:59 off-line

输出:

CYJJ 01
01:05:59 01:07:00 61 $12.10
Total amount: $12.10
CYLL 01
01:06:01 01:08:03 122 $24.40
28:15:41 28:16:05 24 $3.85
Total amount: $28.25
aaa 01
02:00:01 04:23:59 4318 $638.80
Total amount: $638.80

思路和坑点

此题主要关系到数据的过滤,从一堆数据中获得需要的有效数据;然后是时间换算,计算时间差的方式直接使用hash的思想把时间映射成整型,然后累计时间计费。
  数据过滤:把每个人的记录排序,如果遇到两个相邻的配对记录为一条有效记录,如果当前用户有有效记录则输出。否则不输出。
  时间处理:时间的处理映射成整型的分钟数,对每一对儿有效的记录,遍历分一分钟,将每一分钟转换为相应的时段,按照收费标准进行累加。在对时间进行排序的时候可以添加相应的标记作为on-line和off-line标记,此处在时间字符串的结尾加上“+”表示on,用“-”表示off。在进行时间换算的时候使用sscanf读取字符串的时间。从而过滤掉标记。注意sscanf的第一个参数必须是字符数组。
  坑点:
  要注意没有消费记录的顾客不用输出。

AC代码

#include<bits/stdc++.h>
using namespace std;
vector<string> v[1005];                                 //记录数据的二维数组,第一维是序号,第二维是每一个人的记录数组 
int rate[24];                                           //分段计费标准 
int print_c(string a,string b){                         //计算两条记录的费用 
    double ret=0;
    int mn,dn,hn,mm;
    sscanf(a.c_str(),"%d:%d:%d:%d",&mn,&dn,&hn,&mm);    //读取起始时间和结束时间 
    int st=(dn-1)*24*60+hn*60+mm;
    sscanf(b.c_str(),"%d:%d:%d:%d",&mn,&dn,&hn,&mm);
    int ed=(dn-1)*24*60+hn*60+mm;
    for(int i=st;i<ed;i++){                             //根据样例,计费规则,左开右闭 
        ret+=rate[i/60%24];
    }
    printf("%s %s %d $%.2f\n",a.substr(3,8).c_str(),b.substr(3,8).c_str(),ed-st,ret/100);
    return ret;                                         //输出结果,并返回词条记录的计费加入到总计中 
}
int main(void){
    for(int i=0;i<24;i++){
        scanf("%d",&rate[i]);
    }
    int n;        scanf("%d",&n);
    map<string,int> mpdex;                              //map映射每一个id对应记录的位置 
    int k=0;    string mon;                             //记录不同记录的位置和 记录的月份 
    for(int i=0;i<n;i++){
        string id,time,tag;
        cin>>id>>time>>tag;
        if(i==0)    mon=time.substr(0,2);               //月份是一样的,获得一次就够了 
        if(mpdex.find(id)==mpdex.end()){                //分配每一个新纪录id在记录数组的下标 
            mpdex[id]=k++;
        }
        int j=mpdex[id];
        if(tag=="on-line") v[j].push_back(time+"+");    //添加记录标记 
        else            v[j].push_back(time+"-");    
    }
    for(auto it=mpdex.begin();it!=mpdex.end();it++){    //mp自身有序,一次遍历输出 
        int cnt=0;                                      //控制是否有消费记录 
        int j=it->second;
        double total=0;
        sort(v[j].begin(),v[j].end());                  //排序当前顾客的消费记录 
        for(int i=0;i<v[j].size()-1;){
            if(v[j][i].back()=='+'&&v[j][i+1].back()=='-'){ //如果有有效记录,则输出 
                if(cnt++==0)
                    cout<<it->first<<' '<<mon<<'\n';
                total+=print_c(v[j][i],v[j][i+1]);      //输出记录并累加 
                i+=2;                                   //处理一对儿有效记录后移两位 
            }
            else i++;                                   //否则移动一位 
        }
        if(cnt)
            printf("Total amount: $%.2f\n",total/100);  //输出当前顾客的总计 
    }
    return 0;
} 

1017

样例

输入:

7 3
07:55:00 16
17:00:01 2
07:59:59 15
08:01:00 60
08:00:00 30
08:00:02 2
08:03:00 10

输出:

8.2

思路和坑点

将有效的顾客建立队列,这里利用map容器的有序性,直接使用map记录每位顾客,从而形成有序的队列,映射的值为顾客的时间花费,按照要求,每位顾客最长为60分钟,输入时直接处理。
  另一个问题在于窗口的模拟,窗口使用数组记录每个窗口服务结束的时间,如果窗口到达结束时间,而且队伍不空,且队伍中的顾客已经到了,则入队服务并计算等待时间。
  坑点
  判定时间结束的的标志,应该是顾客队列空,而不是17点。因为所有的有效记录都是需要服务的,所以有可能有人17点之前到了,但是排到17点以后才接受服务,测试点5的情况便是如此。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define st 8*3600
#define ed 17*3600
int main(){    
#ifdef ONLINE_JUDGE    
#else    
    freopen("1.txt", "r", stdin);    
#endif
    int n,k;
    cin>>n>>k;
    map<int,int> custom;                            //顾客队列 
    vector<int> window(k,st);                       //窗口模拟 
    for(int i=0;i<n;i++){                           //输入 
        int h,m,s,cost;
        scanf("%d:%d:%d %d",&h,&m,&s,&cost);
        int begin=h*3600+m*60+s;
        if(begin<=ed){                              //有效记录才保存 
            cost=cost>60?3600:cost*60;              //服务时间上界判定 
            custom[ begin]=cost;                     //保存记录
        }
    }
    if(custom.size()==0){                           //队列如果没有人特判,因为0人无法平均 
        cout<<"0.0";
    }
    else{
        n=custom.size();
        int cnt=0;
        int flag=0;
        for(int time=st;;time++){                   //判定终点应该是顾客队列位空,而不是银行关门时间 
            for(int i=0;i<k;i++){
                if(custom.size()==0){               //队列空是退出循环 
                    flag=1;break;
                }
                if(window[i]<=time){                //窗口时间到时且队列中有人时 
                    auto it=custom.begin();         //去队首顾客进行服务 
                    if(it->first<=time){
                        cnt+=time-it->first;        //计算等待时间 
                        window[i]=time+it->second;  //更新窗口结束时间 
                        custom.erase(it);           //将纳入服务的顾客移出队列 
                    }
                }
            }
            if(flag==1)                            //循环结束判定,两层,因此判定两次 
                break;
        }
        
        printf("%.1f",1.0*cnt/(60*n));            
    }
    return 0;    
}

1018

样例

输入:

10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1

输出:

3 0->2->3 0

思路和坑点

Dijkstra+DFS计算出最短路径,然后根据要求进行优化.
  最短路径的算法可以使用模板,根据另外的尺度进行优化比较复杂。但是实际比较好处理,对于获得的一条临时最短路径,做以下分析。假设从管理站0出发的时候不带自行车,也就是携带量bring=0,然后每次经过一个站,如果需要补充就减去,如果需要回收就做加上,那么整个路径上bring最小的时候的绝对值便是需要的自行车数量,如果都是回收的,最后bring依然是0,也就是不用带自行车。最后考虑待带回的自行车,遍历路径的时候计算路径上的总共自行车cnt,最后带上出发时候的自行车就是总共最后剩余的。根据这两个优化尺度获得最终的结果。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 505
#define INF 0x3fffffff
int G[MAXN][MAXN];                      //图 
bool vis[MAXN]={false};                 //遍历标记 
int dis[MAXN];                          //距离数组 
int c,n,sp,m;                         
int biken[MAXN]={INF};                  //每个站点的自行车数量,0号站记为无穷大,其实用不到,但是为了避免误用,这样初始化易于发现错误 
vector<int> path,temppath;              //路径数组和前驱数组 
vector<int> pre[MAXN];
int op1=INF,op2=INF;                    //第二和第三尺度优化
void D(){                               //dijkstra算法模板 
    fill(dis,dis+MAXN,INF);
    dis[0]=0;
    for(int i=0;i<n+1;i++){
        int u=-1,MIN=INF;
        for(int j=0;j<n+1;j++){
            if(vis[j]==false&&dis[j]<MIN){
                u=j;
                MIN=dis[j];
            }
        }
        if(u==-1) return;
        vis[u]=true;
        for(int v=0;v<=n+1;v++){
            if(vis[v]==false&&G[u][v]!=INF){
                if(dis[u]+G[u][v]<dis[v]){
                    dis[v]=dis[u]+G[u][v];
                    pre[v].clear();
                    pre[v].push_back(u);
                }
                else if(dis[u]+G[u][v]==dis[v])
                    pre[v].push_back(u);
            }
        }
    }
}
void dfs(int u){                        //对获得的每一条路径进行dfs方式遍历 
    if(u==0){                           //得到一条路径时进行优化 (dfs边界) 
        int bring=0,cnt=0;              //临时的优化变量 
        for(int i=temppath.size()-1;i>=0;i--){
            cnt+=biken[temppath[i]]-c/2;
            bring=min(bring,cnt);
        }
        bring=bring>=0?0:-bring;        //计算需要带的自行车 
        cnt+=bring;                     //需要带回的自行车 
        if(bring<op1){                  //如果更优,则保存更优结果 
            path=temppath;
            op1=bring; op2=cnt;
        }
        else if(bring==op1&&cnt<op2){
            path=temppath;
            op2=cnt;
        }
        return;
    }
    temppath.push_back(u);              //dfs模板 
    for(int i=0;i<pre[u].size();i++){
        dfs(pre[u][i]);
    }
    temppath.pop_back();
}
int main(void){
    fill(G[0],G[0]+MAXN*MAXN,INF);
    scanf("%d%d%d%d",&c,&n,&sp,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&biken[i]);
    }
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        G[a][b]=G[b][a]=c;
    }
    D();
    dfs(sp);
    printf("%d 0",op1);                //输出最终结果 
    for(int i=path.size()-1;i>=0;i--){
        printf("->%d",path[i]);
    }
    printf(" %d",op2);
    return 0;
} 

1019

样例

样例1

输入:

27 2

输出:

Yes
1 1 0 1 1
样例2

输入:

121 5

输出:

No
4 4 1
补充样例

输入:

15 16

输出:

Yes
15

思路和坑点

进制转换和判断是不是回文数。进制转换使用除留余数法,回文数的判断使用头尾指针向中间移动的方式判断。
  坑点
  对于进制大于10的情况,比如15,使用16进制表示是15,属于16进制意义上的单个位数的值,因此属于回文数。(如补充样例所示)考虑到这种情况,存放每一位应该使用int数组而不是字符型数组。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    int n,b;
    scanf("%d%d",&n,&b);
    vector<int> v;
    do{                                //进制转换 
        v.push_back(n%b);
        n/=b;
    }while(n!=0);
    int flag=1,i=0,j=v.size()-1;
    while(i<j){                        //判定回文数 
        if(v[j]!=v[i]){
            flag=0;    break;
        }
        else{
            i++;j--;
        }
    }
    if(flag)    puts("Yes");
    else        puts("No");
    for(int i=v.size()-1;i>=0;i--){    //输出结果 
        if(i!=v.size()-1) putchar(' ');
        printf("%d",v[i]);
    }
    return 0;
} 

1020

样例

输入:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出:

4 1 6 3 5 7 2

思路和坑点

根据后序和中序序列递归建树,然后使用层序遍历输出结果。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef struct node{
    int data;
    struct node *right,*left;
    node(){
        right=left=NULL;
    }
}Node;
vector<int> post,in;
void layorder(Node *root){                    //层序遍历 
    int cnt=0;
    queue<Node*> q;
    q.push(root);
    while(!q.empty()){
        Node *now=q.front();
        q.pop();
        if(cnt++) putchar(' ');
        printf("%d",now->data);
        if(now->left)    q.push(now->left);
        if(now->right)    q.push(now->right);
    }
}
Node * creat(int pl,int pr,int il,int ir){      //根据中序和后序序列递归建树 
    if(pl>pr)    return NULL;                   //递归边界,序列中没有结点时 
    Node *root=new node;
    root->data=post[pr];                        //根结点赋值 
    int k;
    for(k=il;k<=ir;k++){                        //找到根结点在中序的位置 
        if(post[pr]==in[k])
            break;
    }
    int lnum=k-il;                              //计算左子树的结点个数 
    root->left=creat(pl,pl+lnum-1,il,k-1);      //递归建立左右子树 
    root->right=creat(pl+lnum,pr-1,k+1,ir);
    return root;                                //返回建成的树 
}
int main(void){
    int n;
    scanf("%d",&n);
    post.resize(n); in.resize(n);
    for(int i=0;i<n;i++){
        scanf("%d",&post[i]);
    }
    for(int i=0;i<n;i++){
        scanf("%d",&in[i]);
    }
    Node *root=creat(0,n-1,0,n-1);
    layorder(root);
    return 0;
} 

1021

样例

样例1

输入:

5
1 2
1 3
1 4
2 5

输出:

3
4
5
样例2

输入:

5
1 3
1 4
2 5
3 4

输出:

Error: 2 components

思路和坑点

首先判断是否连通,然后如果连通则输出结果,每次对一个顶点进行bfs遍历,得到树高,然后保存优化的结果。(每次遍历时都要重新初始化vis标记数组)

AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 10004
vector<int> G[MAXN];
int n;
bool vis[MAXN];
int bfs(int u){                         //bfs遍历,得到最大层数,也就是深度 
    queue<int> q;
    q.push(u);
    vis[u]=true;
    int layer=0,last=u,tail=u;          //layer表示层数,last表示当前层的最后一个,tail表示下一层的最后一个 
    while(!q.empty()){
        int now=q.front();
        q.pop();
        int i;
        for(i=0;i<G[now].size();i++){
            int v=G[now][i];
            if(vis[v]==false){
                q.push(v);
                vis[v]=true;
                tail=v;
            }
        }
        if(now==last){                  //当前层结束后,更新层数 
            layer++;
            last=tail;
        }
    }
    return layer;
}
int bfsTrave(){                         //bfs驱动函数,返回连通分量 
    int cnt=0;
    for(int i=1;i<=n;i++){
        if(vis[i]==false){
            cnt++;
            bfs(i);
        }
    }
    return cnt;
}
int main(void){
    scanf("%d",&n);
    for(int i=0;i<n-1;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
    int cnt=bfsTrave();
    if(cnt>1){                          //不连通时 
        printf("Error: %d components",cnt);
    }
    else{                               //连通时计算结果 
        vector<int> v;
        int maxlayer=-1;
        for(int i=1;i<=n;i++){
            fill(vis,vis+MAXN,false);   //每次初始化访问标记数组 
            int temp=bfs(i);
            if(temp>maxlayer){          //如果得到了更高的层数,更新结果数组 
                v.clear();
                v.push_back(i);
                maxlayer=temp;
            }
            else if(temp==maxlayer)     //如果等于当前最高的层数,则保存结果 
                v.push_back(i);
        }
        for(int i=0;i<v.size();i++){
            printf("%d\n",v[i]);
        }
    }
    return 0;
} 

1022

样例

输入:

3
1111111
The Testing Book
Yue Chen
test code debug sort keywords
ZUCS Print
2011
3333333
Another Testing Book
Yue Chen
test code sort keywords
ZUCS Print2
2012
2222222
The Testing Book
CYLL
keywords debug book
ZUCS Print2
2011
6
1: The Testing Book
2: Yue Chen
3: keywords
4: ZUCS Print
5: 2011
3: blablabla

输出:

1: The Testing Book
1111111
2222222
2: Yue Chen
1111111
3333333
3: keywords
1111111
2222222
3333333
4: ZUCS Print
1111111
5: 2011
1111111
2222222
3: blablabla
Not Found

[buy]本题借鉴柳婼的解题思路。[/buy]

思路和坑点

对不同的查询信息,建立对应的存储表。不同的查询字对应不同的存储表,利用set的自身有序性和不重复的特征存储查询表,并使用map容器把每一类关键字映射到一个set容器,查询时应当先判定是否有该记录。
  读取每本书的信息,参见代码部分的读入。

AC代码

#include<bits/stdc++.h>
using namespace std;
unordered_map<string,set<int> > title,name,key,pu,py;               //查询要用到的索引表 
void getans(unordered_map<string,set<int> > &mp,string &check){     //查询函数,传引用,对不同的查询字进行查询 
    if(mp.find(check)==mp.end())                                    //如果找不到 
        puts("Not Found");
    else{
        for(auto it=mp[check].begin();it!=mp[check].end();it++)     //如果找到,则遍历输出,set自身有序 
            printf("%07d\n",*it);
    }
}
void getbook(){                                                     //读取一本书的记录 
    int id;
    string _title,_name,_key,_pu,_py;                                
    scanf("%d",&id);    getchar();                                  //读取id后,要过滤换行符 
    getline(cin,_title);    title[_title].insert(id);               //整行读取书名和人名 
    getline(cin,_name);        name[_name].insert(id);
    while(1){                                                       //分别读取关键词,并单独处理 
        cin>>_key;    key[_key].insert(id);        
        char ch=getchar();                                          //吸收空格,如果到了结尾的回车,关键字读取结束 
        if(ch=='\n')    break;
    }
    getline(cin,_pu);    pu[_pu].insert(id);                        //整行读取出版商和出版时间 
    getline(cin,_py);    py[_py].insert(id);
}
int main(void){
    int n;
    scanf("%d",&n);    getchar();
    for(int i=0;i<n;i++){
        getbook();
    }
    scanf("%d",&n);    getchar();
    for(int i=0;i<n;i++){
        string str;
        getline(cin,str);                                           //整行读入查询语句 
        cout<<str<<'\n';
        string check=str.substr(3,str.size()-3);                    //摘取查询字 
        switch(str[0]){                                             //根据查询类型分类查询 
            case '1':getans(title,check);break;
            case '2':getans(name,check);break;
            case '3':getans(key,check);break;
            case '4':getans(pu,check);break;
            case '5':getans(py,check);break;
        }    
    }
    return 0;
} 

1023

样例

输入:

1234567899

输出:

Yes
2469135798

思路和坑点

大数相加,使用string类进行运算,这个代码块可以直接记忆。另外,使用map存放两个数中每一位的个数,然后如果两个map相同则说明Yes,否则No。
  坑点
  大数相加,最后注意,如果最后一位有进位,一定要算上。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    string str,out;
    getline(cin,str);
    map<int,int> a,b;
    int flag=0,temp;
    for(int i=str.size()-1;i>=0;i--){
        temp=str[i]-'0';            
        a[temp]++;                  //临时存放单个位乘2之后的结果
        temp*=2; temp+=flag;        //flag是进位
        out.push_back(temp%10);
        b[temp%10]++;
        flag=temp/10;
    }
    if(flag>0){                     //最后如果还有进位,需要记得算上,不要丢掉
        b[flag]++;
        out.push_back(flag);
    }
    if(a==b)    puts("Yes");
    else         puts("No");
    for(int i=out.size()-1;i>=0;i--)
        printf("%d",out[i]);    
        
    return 0;
} 

1024

样例

样例1

输入:

67 3

输出:

484
2
样例2

输入:

69 3

输出:

1353
3

思路和坑点

大数相加,直接使用模板进行相加,然后判定是不是回文数,如果是提前结束,否则直到最后规定的步数。

AC代码

#include<bits/stdc++.h>
using namespace std;
bool isp(string str){
    int i=0,j=str.size()-1;
    while(i<j){
        if(str[i]!=str[j])
            return false;
        i++;j--;
    }
    return true;
}
void doadd(string &str){                            //大数相加,使用传引用直接修改 
    string temp;
    int flag=0;
    for(int i=0,j=str.size()-1;j>=0;i++,j--){
        int tag=str[i]-'0'+str[j]-'0'+flag;
        temp.push_back(tag%10+'0');
        flag=tag/10;
    }
    if(flag>0) temp.push_back(flag+'0');
    reverse(temp.begin(),temp.end());
    str=temp;                                        //重新赋值 
}
int main(void){
    string a;
    int k,i=0;
    cin>>a>>k;
    for(i=0;i<k;i++){
        if(isp(a))    break;
        doadd(a);
    }
    cout<<a<<'\n'<<i;
    return 0;
} 

1025

样例

输入:

2
5
1234567890001 95
1234567890005 100
1234567890003 95
1234567890002 77
1234567890004 85
4
1234567890013 65
1234567890011 25
1234567890014 100
1234567890012 85

输出:

9
1234567890005 1 1 1
1234567890014 1 2 1
1234567890001 3 1 2
1234567890003 3 1 2
1234567890004 5 1 4
1234567890012 5 2 2
1234567890002 7 1 5
1234567890013 8 2 3
1234567890011 9 2 4

思路和坑点

使用结构体的多级排序,每次排序后更新名次。这里采用了排下标的方法来减少时间花费,详情可以参加PAT总结中关于结构体部分的总结。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef struct node{                        //学生结构体 
    string id;
    int score,local,lr;
}Node;
vector<Node> v;
vector<int> idsort;                         //用于按照下标排序 
int tag=1;
bool cmp(int i,int j){                      //多级排序,tag=1时,优先按照考场排,然后确定考场排名 
    if(tag==1){
        if(v[i].local!=v[j].local) return v[i].local<v[j].local;
        else if(v[i].score!=v[j].score) return v[i].score>v[j].score;
        else     return v[i].id<v[j].id;
    }
    else{
        if(v[i].score!=v[j].score)    return v[i].score>v[j].score;
        else return v[i].id<v[j].id;        //tag不等于1时,采用总排名的规则 
    }
}
int main(void){
    int n,k,cnt=0;;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&k);
        for(int j=0;j<k;j++){
            Node temp;
            cin>>temp.id;
            scanf("%d",&temp.score);
            temp.local=i;
            idsort.push_back(cnt++);
            v.push_back(temp);
        }
    }
    sort(idsort.begin(),idsort.end(),cmp);    
    int pre=idsort[0]; v[pre].lr=1; cnt=1;      //第一个优先处理 ,cnt用于更新排名 
    for(int i=1;i<idsort.size();i++){
        int index=idsort[i];
        cnt++;
        if(v[index].local==v[pre].local){       //如果当前的与前边同一考场 
            if(v[index].score==v[pre].score)
                v[index].lr=v[pre].lr;
            else
                v[index].lr=cnt;
        }
        else{                                   //如果不同考场 
            v[index].lr=1;    cnt=1;
        }
        pre=index;
    }
    tag=0;
    sort(idsort.begin(),idsort.end(),cmp);      //总排名一遍输出,一遍确定名次 
    cnt=1;
    printf("%d\n",idsort.size());
    pre=idsort[0];
    cout<<v[pre].id<<' '<<1<<' '<<v[pre].local<<' '<<1<<'\n';
    for(int i=1;i<idsort.size();i++){
        int index=idsort[i];
        cout<<v[index].id<<' ';
        if(v[index].score!=v[pre].score)
            cnt=i+1;
        printf("%d %d %d\n",cnt,v[index].local,v[index].lr);
        pre=index;
    }
    return 0;
} 

1026

样例

输入:

9
20:52:00 10 0
08:00:00 20 0
08:02:00 30 0
20:51:00 10 0
08:10:00 5 0
08:12:00 10 1
20:50:00 10 0
08:01:30 15 1
20:53:00 10 1
3 1
2

输出:

08:00:00 08:00:00 0
08:01:30 08:01:30 0
08:02:00 08:02:00 0
08:12:00 08:16:30 5
08:10:00 08:20:00 10
20:50:00 20:50:00 0
20:51:00 20:51:00 0
20:52:00 20:52:00 0
3 3 2

思路和坑点

服务队列模拟,使用结构体数组的方式表示多个桌子,每个桌子结构体中包含桌子当前服务的结束时间,桌子服务的人数统计。顾客分成vip和非vip队列,这里利用map容器的成对儿存放,并且自身有序,因此用其表示顾客队列。
  坑点
  桌子服务的判定标准是,队列中有人,且已经在等候。桌子空闲的标志是该桌子当前服务结束时间小于等于当前的时间。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define st 8*3600
#define ed 21*3600

typedef struct table{                       //桌子结构体 
    int edtime,cnt,tag;                     //当前服务的结束时间,服务人数统计,vip标记 
    table():edtime(st),cnt(0),tag(0){}      //初始化 
}Table;
void Print(int i){
    printf("%02d:%02d:%02d",i/3600,i/60%60,i%60);    //打印时间 
}
vector<Table> T;                            //桌子的数组 
map<int,int> mpo,mpv;                       //map表示的顾客服务队列 
void check(int j,int time,map<int,int> &mp){    //使用传引用的方式,对不同的队列进行判断是都要从中取出顾客进行服务 
    if(mp.size()){                          //非空 
        auto it=mp.begin();                    
        if(it->first<=time){                //队头顾客已经在等待,进行服务,打印并更新队列参数 
            T[j].edtime=time+it->second;
            T[j].cnt++;
            Print(it->first); putchar(' ');
            Print(time);           putchar(' '); 
            printf("%d\n",(time-it->first+30)/60);
            mp.erase(it);
        }
    }
}
int main(void){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){                   //读入 
        int hh,mm,ss,cost,tag;
        scanf("%d:%d:%d %d %d",&hh,&mm,&ss,&cost,&tag);
        cost=cost>120?120*60:cost*60;
        if(tag==0)
            mpo[hh*3600+mm*60+ss]=cost;    //普通用户入队 
        else    
            mpv[hh*3600+mm*60+ss]=cost;    //vip用户入队 
    }
    int k,m;
    scanf("%d %d",&k,&m);
    T.resize(k);
    for(int i=0;i<m;i++){                   //vip桌子的标记 
        int temp;
        scanf("%d",&temp);
        T[temp-1].tag=1;
    }
    for(int i=st;i<ed;i++){
        for(int j=0;j<k;j++){               //优先遍历一遍为vip顾客进行服务判定 
            if(T[j].edtime<=i&&T[j].tag==1){
                check(j,i,mpv);
                if(mpv.size()==0) break;    //vip服务全部完成时,提前结束循环 
            }
        }
        for(int j=0;j<k;j++){               //安排了vip之后,全员进行判定 
            if(T[j].edtime<=i){
                if(mpo.size()&&mpv.size()){ //如果都不空,取来的早的进行判定 
                    auto it=mpo.begin(),itv=mpv.begin();
                    if(it->first<itv->first)
                        check(j,i,mpo);
                    else if(it->first>itv->first)
                        check(j,i,mpv);
                }
                else if(mpo.size())         //如果有一个空,非空的进行判定 
                    check(j,i,mpo);
                else if(mpv.size())
                    check(j,i,mpv);
                else break;                  //都空则,提前结束,表示顾客已经服务完毕 
            }
        }
        if(mpo.size()==0&&mpv.size()==0)    break;
    }
    for(int i=0;i<k;i++){                   //打印服务人数 
        if(i) putchar(' ');
        printf("%d",T[i].cnt);
    }
    return 0;
} 

1027

样例

输入:

15 43 71

输出:

#123456

思路和坑点

进制转换,而且只有两位数,直接使用/和%两个操作确定每一位的数值,可以直接建立每一位数值的映射表,或者直接用字符运算输出。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    putchar('#');
    for(int i=0;i<3;i++){
        int temp;
        scanf("%d",&temp);
        char a,b;
        a=temp/13>9?(temp/13-10+'A'):(temp/13+'0');
        b=temp%13>9?(temp%13-10+'A'):(temp%13+'0');
        putchar(a);putchar(b);
    }
    return 0;
} 

1028

样例

样例1

输入:

3 1
000007 James 85
000010 Amy 90
000001 Zoe 60

输出:

000001 Zoe 60
000007 James 85
000010 Amy 90
样例2

输入:

4 2
000007 James 85
000010 Amy 90
000001 Zoe 60
000002 James 98

输出:

000010 Amy 90
000002 James 98
000007 James 85
000001 Zoe 60
样例3

输入:

4 3
000007 James 85
000010 Amy 90
000001 Zoe 60
000002 James 90

输出:

000001 Zoe 60
000007 James 85
000002 James 90
000010 Amy 90

思路和坑点

简单的结构体多级排序,这里考虑到数据量比较大,使用下标排序的方法。对于不同的排序规则,使用全局变量c来控制排序规则。

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,c;
typedef struct node{
    string id,name;
    int score;
}Node;
vector<Node> v;
vector<int> idsort;
bool cmp(int a,int b){
    if(c==1)
        return v[a].id<v[b].id;
    else if(c==2){
        if(v[a].name!=v[b].name)    return v[a].name<v[b].name;
        else                        return v[a].id<v[b].id;
    }
    else{
        if(v[a].score!=v[b].score)    return v[a].score<v[b].score;
        else                        return v[a].id<v[b].id;
    }
}
int main(void){
    scanf("%d%d",&n,&c);
    v.resize(n); idsort.resize(n);
    for(int i=0;i<n;i++){
        idsort[i]=i;
        cin>>v[i].id>>v[i].name>>v[i].score;        
    } 
    sort(idsort.begin(),idsort.end(),cmp);
    for(int i=0;i<n;i++){
        int k=idsort[i];
        cout<<v[k].id<<' '<<v[k].name<<' '<<v[k].score<<'\n';
    }
    return 0;
} 

1029

样例

样例1

输入:

4 11 12 13 14
5 9 10 15 16 17

输出:

13
补充样例

输入:

1 1
1 2

输出:

1

思路和坑点

考虑到数据量比较大,而且直接合并排序的时间比较长,因此单独对每一个进行排序。然后,计算出中位数是第k个,每次从两个序列中弹出一个小的数,直到弹出了k-1个之后,下一个要被弹出的便是中位数。可以使用一个标记来标识在k-1之后只进行一次便推出循环,省去了最后找第k个的的代码重复。
  坑点
  对于每个序列只有一个的情况,需要特判一下,不然我这个循环会因为这种情况陷入死循环。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main(void){
    vector<int> a,b;
    int la,lb,k,cnt=0;
    scanf("%d",&la);    a.resize(la);
    for(int i=0;i<la;i++)
        scanf("%d",&a[i]);
    scanf("%d",&lb);    b.resize(lb);
    for(int i=0;i<lb;i++)
        scanf("%d",&b[i]);
    if(la==1&&lb==1){                       //特判每一个序列只有一个数的情况 
        printf("%d",min(a[0],b[0]));
        return 0;
    }
    sort(a.begin(),a.end());                //对两个序列进行排序 
    sort(b.begin(),b.end());
    int i=0,j=0;
    k=(la+lb)%2==0?(la+lb)/2:(la+lb+1)/2;   //算出中位数在数列中位于第几个,从1开始算的第k个 
    while(1){
        if(i<la&&j<lb){                     //如果两个表都未到头,弹出小的那一个 
            if(a[i]<=b[j])    i++;
            else j++;
        }
        else if(i<la)    i++;               //如果有任意一个空了,则从不空的里边弹出一个 
        else if (j<lb)     j++;
        cnt++;
        if(cnt==k-1) break;                 //当弹出k-1个的时候,下一个就是第k个了 
    }
    if(i<la&&j<lb)                          //找到第k个的元素在哪个序列中 
        printf("%d",min(a[i],b[j]));
    else if(i<la)
        printf("%d",a[i]);
    else if(j<lb)
        printf("%d",b[j]);
    return 0;
} 

1030

样例

输入:

4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

输出:

0 2 3 3 40

思路和坑点

两个优化参数的最短路径问题,而且问题的路径答案唯一,直接使用一次Dijkstra算法,同时对两个变量进行优化即可。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 505
#define INF 0x3fffffff
int n,m,s,d;                                //参数 
int dis[MAXN],cost[MAXN],pre[MAXN];         //最短距离数组,最小花费数组,路径前驱数组 
int G[MAXN][MAXN];                          //图 
int C[MAXN][MAXN];                          //花费 
bool vis[MAXN]={false};                     //访问标记 
void D(){                                   //两个参数的Dijkstra算法模板 
    fill(dis,dis+MAXN,INF);
    fill(cost,cost+MAXN,INF);
    dis[s]=cost[s]=0;
    for(int i=0;i<n;i++){
        int u=-1,MIN=INF;
        for(int j=0;j<n;j++){
            if(vis[j]==false&&dis[j]<MIN){
                u=j;
                MIN=dis[j];
            }
        }
        if(u==-1) return;
        vis[u]=true;
        for(int v=0;v<n;v++){
            if(vis[v]==false&&G[u][v]!=INF){
                if(dis[u]+G[u][v]<dis[v]){
                    dis[v]=dis[u]+G[u][v];
                    cost[v]=cost[u]+C[u][v];
                    pre[v]=u;
                }
                else if(dis[u]+G[u][v]==dis[v]&&cost[u]+C[u][v]<cost[v]){
                    cost[v]=cost[u]+C[u][v];
                    pre[v]=u;
                }
            }
        }
    }
}
int main(void){
    fill(G[0],G[0]+MAXN*MAXN,INF);                      //初始化 
    fill(C[0],C[0]+MAXN*MAXN,INF);
    fill(pre,pre+MAXN,-1);
    scanf("%d%d%d%d",&n,&m,&s,&d);                      //读入数据 
    for(int i=0;i<m;i++){                                
        int a,b,d,c;
        scanf("%d%d",&a,&b);
        scanf("%d%d",&G[a][b],&C[a][b]);
        G[b][a]=G[a][b];    C[b][a]=C[a][b];        
    } 
    D();
    vector<int> out;                                    //保存并输出路径 
    out.push_back(cost[d]);    out.push_back(dis[d]);   //最短距离和最小花费一并放入输出结果的数组中 
    while(d!=-1){
        out.push_back(d);
        d=pre[d];
    }
    for(int i=out.size()-1;i>=0;i--){
        if(i!=out.size()-1) putchar(' ');
        printf("%d",out[i]);
    }
    return 0;
} 
  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值