这是我的题解,使用不是很熟练,请多理解指正。
BY:王子豪
做题页面链接:HPU算法协会公开课第一期:【基础算法1】
A - 前m大的数
特点:考察了对时间复杂度的理解。
思路:找出开数组的关键大小;思考,如何才能快速的找到需要的目标数字,发现使用sort可以先排序然后倒着找。在sort之前用冒泡把每两个的和存起来。
注意:开数组在main之外,并且输出的时候注意最后一个数据换行!
#include<iostream> #include<algorithm> using namespace std; int a[3005];//如果最大3000个数据*3000 要9百万/2 开数组是需要开一半就够了 int N,M;//初始化 int sum[5000000];//存取给出的数字的和 int main(){ while(cin>>N>>M){ for(int i=1;i<=N;i++){ scanf("%d",&a[i]); } int cnt=1;//记录位数 从1开始存 for(int i=1;i<=N;i++){ for(int j=i+1;j<=N;j++){ sum[cnt++]=a[i]+a[j]; } } sort(sum,sum+cnt); for(int i=cnt-1,z=M;i>0;i--,z--){ if(z==1) { printf("%d\n",sum[i]);break; } else printf("%d ",sum[i]); }} return 0; }
B - 稳定排序
特点:细节上增大了一点对结构体排序的认识,引入名词“不稳定排序”,事实上大多数排序都是不稳定排序。
思路:定义结构体,正常判断。 可以用一个计数器来最终判断是不稳定排序,还是正确的。
注意:判断情况1:不稳定情况,是满足分数对应,名字不对应。
判断情况2:失败情况,分数不对应,名字也不对应。
情况3:分数名字都不对应。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; struct student{ int score;//成绩 int count; char name[55];//名字 }a[350],b[350]; int cmp(student x,student y) { if(x.score!=y.score) return x.score>y.score; else return x.count<y.count; //先返回序号在前的 } int main() { //排序首先是按照分数 假定排序后 先判断名字 就可以做到是不是稳定的,如果连名字都不对 //肯定都不是正确的了 假设分数对 名字不对 就是不稳定的 int n; while(~scanf("%d",&n)) { //清空 memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for(int i=0;i<n;i++) { scanf("%s %d",&a[i].name,&a[i].score); a[i].count=i; //记录序号 } sort(a,a+n,cmp); //正规的排序之后 int q=1,w=1; //设置变量 判断 q判断名字同样 w判断分数重 for(int i=0;i<n;i++) { scanf("%s %d",&b[i].name,&b[i].score); if(strcmp(a[i].name,b[i].name)!=0) //排序之后每一个都不对应,说明可能不稳定 q=0; //名字这个不相同但是后面的人数都是对应的 if(a[i].score!=b[i].score) w=0; } if(q+w==2) printf("Right\n"); else if(q==0&&w==1) //不稳定 名字对应分数不对应 { printf("Not Stable\n"); for(int i=0;i<n;i++) printf("%s %d\n",a[i].name,a[i].score); } else //分数都不对应 { printf("Error\n"); for(int i=0;i<n;i++) printf("%s %d\n",a[i].name,a[i].score); } } return 0; }
C - 开门人和关门人
特点:考察了结构体排序问题,加上时间转换和细节。
思路:正常排序。
#include<iostream> #include<algorithm> #include<cmath> using namespace std; struct person{ char name[20]; // char time[100]; int time1; int time2; }a[1000]; bool cmp1(person a, person b) { return a.time1 < b.time1; } bool cmp2(person a, person b) { return a.time2 > b.time2 ; } int main(){ int N,M;//记录数据 int h,m,s;//记录时:分:秒 最后换算一下 scanf("%d",&N); while(N--){ scanf("%d",&M); for(int i=0;i<M;i++){ scanf("%s %d:%d:%d", &a[i].name,&h,&m,&s);//按照格式输入名字时间分时秒 a[i].time1=s+m*60 + h*3600; //第一天的开始时间 用s算 scanf("%d:%d:%d",&h,&m,&s);//第一天的结束就时间 a[i].time2=s+m*60 + h*3600; } // 排序(按开门时间) sort(a, a+M, cmp1); // 输出开门人名字 找最早开门 cout << a[0].name << " "; // 排序(按关门时间) 找最晚开门 sort(a, a+M, cmp2); cout << a[0].name << endl; } return 0; }
D - EXCEL排序
特点:这里突然想到了,可以用c++中的string直接进行比较。不再使用strcmp进行返回值比较
#include<iostream> #include<algorithm> #include<math.h> #include<cstring> using namespace std; struct student{ char number[8]; char name[10];//名字 int score;//成绩得分 }a[111111];; //开始定义cmp bool cmp1(student a,student b){ //在第一种比较情况下,使用字符串比较,先返回字符小的,也就是递增排序 return strcmp(a.number,b.number )<0; //这里把学号存成字符形式 不要yint的 } bool cmp2(student a,student b){ if(strcmp(a.name ,b.name)!=0) { return strcmp(a.name,b.name)<0;//非递减,先返回小的 } else { return cmp1(a,b);//两个名字相等 就按第一种递增的 } } bool cmp3(student a,student b){ if(a.score!=b.score ) { return a.score <b.score ; } else { return cmp1(a,b); } } int main(){ int i,j,n,m,count=0; while(cin>>n>>m&&n){ for(i=0;i<n;i++){ cin>>a[i].number>>a[i].name>>a[i].score; } //正常结构体读取 //开始判断 怎么进行排序 if(m==1){ sort(a,a+n,cmp1); } if(m==2){ sort(a,a+n,cmp2); } if(m==3){ sort(a,a+n,cmp3); } cout<<"Case "<<++count<<":"<<endl; for(i=0;i<n;++i) { cout<<a[i].number<<" "<<a[i].name<<" "<<a[i].score; if(i!=n) cout<<endl; } } return 0; }
F - 水果
特点:可以使用结构体,但是比较麻烦,而且不是很熟练,不如接受一个新的STL内容-> map容器
map特点:1,其基本单位(节点)为pair类型,就是必须有实值(value)和键值。pair的第一元素为键值(通过.first 或->first访问),第二元素为实值(通过.second 或者->second访问)
2,map不容许有相同的键值
3,map中的所有元素(value)都根据键值(key)被自动排序。
4,键值不可以被修改
补充:c++ 里面的map容器的迭代器里面 有个first 和 second
例如
map<string, int> m;
m["one"] = 1;
map<string, int>::iterator p = m.begin();
p->first; // 这个是 string 值是 "one"
p->second; //这个是 int 值是 1
思路:容器套容器,在一个map容器里面放入一个map容器,读取的时候采用二位数组,先读取地区,在读取水果名字,塞进去容器,自动排序。访问的时候就分别定义一,二节迭代器访问其中已经排序好的,每一个first都对应着一个储存水果类型和个数的map<string,int>。
#include <iostream> #include <map> #include <stdio.h> #include <string> using namespace std; string s1,s2; int main() { //map< string,map<string,int> >mmp; //map<string,int>mp; map< string,map<string,int> > p; int t,n,num,flag = 0; cin>>t; while(t--){ p.clear(); cin>>n; while(n--){ cin>>s2>>s1>>num; p[s1][s2]+=num; //s1是地区 s2是水果种类 连带着水果种类和数量 } map< string,map<string,int> >::iterator iter1;//地区容器+ map<string,int>::iterator iter2;//水果容器 在地区容器的第二个 for(iter1 = p.begin(); iter1!= p.end();iter1++){ cout<<iter1->first<<endl;//先输出地区 也就是第一个迭代器的最开头的一个 for(iter2 = iter1->second.begin();iter2 != iter1->second.end();iter2++){ cout<<" |----"<<iter2->first<<"("<<iter2->second<<")"<<endl;//在地区里面读取 //输出格式 并且读取 } } if(t){ cout<<endl; } } return 0; //fclose(stdin); }
G - 不重复数字
特点:set结构体去重,使用count查找,返回值类型是0/1.
因为set容器会直接进行排序,所以思路改变一下:把数据存入数组中,同时也存进去set,每次存入的数据都用count查找一下,如果饭hi之是0,在输出这个输入的数据,并且注意
#include<iostream> #include<set> #include<algorithm> #include<stdio.h> using namespace std; int a[50005]; int main(){ set<int>q; int N,m,x,z=0; scanf("%d",&N); while(N--){ q.clear(); scanf("%d",&m); // int count=0; for(int i=0;i<m;i++){ scanf("%d",&a[i]); z=a[i]; //这里count返回值是一个证书,1代表有0无 if(q.count(z)==0){ // if(count>0) //判断是不是第一个数字 // printf(" "); //前面的是判断的是 是不是第一位 输出空格 不是第一位的都要出一个空格 printf("%d ",z); // count++; q.insert(z); } } printf("\n"); } return 0; }
H - 表达式括号匹配
特点:栈典型应用。
代码1:数组模仿栈:
#include<stack> #include<iostream> #include<cstring> using namespace std; int judge(char s[]){ char stack[50000]; int top=-1; int i; int len=strlen(s); for(i=0;i<len;i++){ if(s[i]=='('){ stack[++top] = '('; } if(s[i] == ')') { if (top == -1) { return 0; } else --top; } } if (top == -1) return 1; else return 0; } int main(){ char s[2005]; scanf("%s",&s); if(judge(s)){ printf("YES"); } else printf("NO"); }
代码二:stack
#include<iostream> #include<stack> #include<cstring> using namespace std; stack<char>st; string s; bool judge(){ int len=s.size(); int c=1; for(int i=0;i<len;i++){ if(st.empty()&&s[i]==')'){ return 0; c=0; break; } if(s[i]=='('){ st.push(s[i]); } if(s[i]==')'&&!st.empty()){ st.pop(); } } if(st.empty()&&c!=0) return 1; else return 0; } int main(){ cin>>s; if(judge()){ printf("YES"); } else printf("NO"); return 0; }
I - 合并果子
思路,用queue队列中的优先队列做。
特点:priority_queue <int>q;</int>
因为合并果子是贪心算分典型,要先合并堆数字比较小的,但是优先队列是大根堆,如果压入1,2,3他会先合并2,3所以我们可以压入符数-1-22-3,这样县合并-1-2这样的话,我们就可以用减法求体力了。这是杜皓轩教给我的,谢谢他。
#include<iostream> #include<queue> #include<algorithm> using namespace std; priority_queue <int>q; int main(){ int t,n,z,sum,s; scanf("%d",&t); while(t--){ sum=0; s=0; // while (!q.empty()) q.pop(); cin>>n; for(int i=0;i<n;i++){ cin>>z; q.push(-z); } for(int i=1;i<n;i++){ s=q.top(); q.pop(); s=s+q.top(); q.pop(); q.push(s); sum=sum-s; } printf("%d\n",sum); } return 0; }
K - Ignatius and the Princess IV
伊格内修斯和公主四世
特点:找出一组n个数据中出现次数超过(n+1)/2次数的数据,并且输出
#include<iostream> #include<algorithm> #include<stdlib.h> #include<cstring> using namespace std; const int maxn = 1e6+10; int a[maxn],b[maxn]; int main(){ int n; while(scanf("%d",&n)!=EOF){ memset(b,0,sizeof(b)); for(int i=0;i<n;i++){ scanf("%d",&a[i]); b[a[i]]++; //cout<<b[a[i]]<<" "; } int count=(n+1)/2; for(int i=0;i<n;i++){ //用的这个方法是偶然想到的 以前做过把数组开到堆里面 //所有的数组都是0,然后如果是存在一个就是1,两个就是2; if(b[a[i]]>=count){ cout<<a[i]<<endl; break; } } } return 0; }
思路二
#include <cstdio> #include <cstring> #include<algorithm> #include <iostream> using namespace std; int a[1000000]; int main() { int n; while(scanf("%d",&n)!=EOF) {for(int i=0;i<n;i++) { scanf("%d",&a[i]);} sort(a,a+n); printf("%d\n",a[(n+1)/2]); } return 0; }
M - SnowWolf's Wine Shop
买酒题目,听了高手的讲解才会的,才知道有multiset这个东西。。
#include<iostream> #include<set> #include<algorithm> using namespace std; multiset<int>st; int n,q,y; int xu;//这是需求 每个人需要的那种价格的就睡 int main(){ int t; scanf("%d",&t); int count=0; while(t--){ printf("Case %d:\n",++count); st.clear(); scanf("%d%d%d",&n,&q,&y); for(int i=0;i<n;i++){ int z; scanf("%d",&z); st.insert(z); } while(q--){ scanf("%d",&xu); multiset<int>::iterator it=st.lower_bound(xu); if(it==st.end()||*it-y>xu){ printf("-1\n"); } else{ printf("%d\n",*it); st.erase(it);//删除的是这个位置 用迭代器位置 } } } return 0; }