map专题

61. 火星数字(1011)

本题要求实现火星数字和地球数字间的转换,核心任务有:

(1)如何读入,N给定了读入数据个数,但是每个数据有几位数,是数字还是字母不确定;

——>同一用字符串读取,由于中间可能有空格,采用getling()
(2)映射关系是双向的,从数字到单词和单词到数字的映射都有

——>根据首字符判断输入的是地球数字还是火星文,地球数字到火星文可以用字符串数组,而从火星文到地球数字用map。

从地球数字到火星文简单,只需做一个进制转化去字符串数组中查找即可。从火星文到地球数字根据是否有空格分类讨论,有空格说明是两位数(除了13的倍数,这个要特别判断)。

注意:13的倍数输出不能带tret(火星0);

#include<iostream>
#include<cstdio>
#include<string>
#include<map>
using namespace std;
//数字映射到字符串
string thirteen[13]={"tret","jan","feb", "mar", "apr", "may","jun", "jly", "aug", "sep", "oct", "nov", "dec"};
string times[12]={"tam", "hel", "maa", "huh", "tou", "kes","hei", "elo", "syy", "lok", "mer", "jou"};
//字符串映射到数字
map<string,int> mars13,mars13time;void mapping(){//实现字符串到数字的映射for(int i=0;i<13;i++){mars13[thirteen[i]]=i;}for(int i=0;i<12;i++){mars13time[times[i]]=(i+1)*13;}}void E_to_M(string s){int num=0;for(int i=0;i<s.length();i++){num=num*10+s[i]-'0';}if(num/13!=0){cout<<times[num/13-1];if(num%13==0) {cout<<endl;return;}else cout<<" ";//还有尾数}        cout<<thirteen[num%13]<<endl;}void M_to_E(string s){string temp1,temp2;if(s.find(" ")!=string::npos){//在s中发现空格,说明有两位数int t=s.find(" ");temp1=s.substr(0,t);temp2=s.substr(t+1,s.length()-t-1);int num=mars13time[temp1];num+=mars13[temp2];cout<<num<<endl;return;}map<string,int>::iterator it=mars13.find(s);if(it!=mars13.end()){int num=mars13[s];cout<<num<<endl;}else{int num=mars13time[s];cout<<num<<endl;}}int main(){mapping();int n;//cin>>n;scanf("%d",&n);getchar();//吸收换行for(int i=0;i<n;i++){string str;getline(cin,str);//cout<<str<<endl;if(str[0]>='0'&&str[0]<='9'){//输入的是数字E_to_M(str);}else{M_to_E(str);}}return 0;
}

语法:(1)mp.find(key),寻找键值的映射值,若找到返回下标,查找失败则返回end();

(2)str.substr(start,len),返回子串,start为起始位置,len为字串长度;

(3)读入带空格字符串使用getline(cin,str)(但舍弃换行符),注意要使用gelchar()先把n读入后的回车吸收掉,不然会当作一条空字符串读入。

当然我的做法其实太过复杂了,对付火星文到数字的转化其实不必分两位考虑,可以直接建立全部字符串与数字的映射关系,把所有的字符串都构造出来存入 map。

#include<iostream>
#include<cstdio>
#include<string>
#include<map>
using namespace std;string thirteen[13]={"tret","jan","feb", "mar", "apr", "may","jun", "jly", "aug", "sep", "oct", "nov", "dec"};
string times[13]={"tret","tam", "hel", "maa", "huh", "tou", "kes","hei", "elo", "syy", "lok", "mer", "jou"};//加上tret便于统一处理string numToStr[170];//数字->火星文
map<string,int> strToNum;//火星文->数字void init(){//实现火星文和数字间的映射for(int i=0;i<13;i++){numToStr[i]=thirteen[i];//个位0-12,十位为0numToStr[i*13]=times[i];//十位为0-12,个位为0strToNum[thirteen[i]]=i;strToNum[times[i]]=i*13;}for(int i=1;i<13;i++){//十位for(int j=1;j<13;j++){//个位,从1开始是因为上一个循环已覆盖13倍数,且火星文13倍数不需要加tretstring str=times[i]+" "+thirteen[j];numToStr[i*13+j] = str;strToNum[str] = i*13+j;}}}int main(){init();int n;scanf("%d",&n);getchar();//吸收换行for(int i=0;i<n;i++){string str;getline(cin,str);//cout<<str<<endl;if(str[0]>='0'&&str[0]<='9'){//输入的是数字int num=0;for(int j=0;j<str.length();j++){num =num*10+str[j]-'0';                }cout<<numToStr[num]<<endl;}else{cout<<strToNum[str]<<endl;}}return 0;
}

62. 主导色彩(1054)

本题本质上是要求找出矩阵中出现次数占一半以上的数据。由于矩阵很大,直接建立矩阵大小的数组很可能超出内存,因此采用map。

最后一个测试点卡了,仔细思考后发现是只有1个元素的数组,初始化映射值后循环结束没有机会输出了,因此加上特判。

#include<iostream>
#include<cstdio>
#include<map>
using namespace std;map<int,int> color;int main(){int n,m;int t;scanf("%d%d",&n,&m);for(int i=0;i<n;i++){for(int j=0;j<m;j++){            scanf("%d",&t);if(n*m==1) {//只有一个元素的矩阵,直接输出printf("%d",t);return 0;}map<int,int>::iterator it =color.find(t);            if(it==color.end()){//第一次出现置位1color[t]=1;}else{color[t]++;if(color[t]>(n*m)/2){printf("%d",t);break;}                }            }}    return 0;
}

不过我在测试过程也发现,即使不给映射值初始化(即只有else那部分),直接++也能AC,似乎映射到int默认初值就是0(待确认)。

63. 说话形式(1071)

本题是要得到输入字符串中出现频率最高的单词及其频率,难点是解决以下问题:

(1)读入,以空格划分,以回车结束。由于cin不能读入回车,无法作为读入终止判断,因此不能直接读入单个单词,采用getline()一次性读入。
(2)统计单词出现次数,不考虑出字母与数字以外符号,不区分大小写;对"Can要跳过"识别出can,因此需要把大写字母化成小写,去掉其余字符,取单个单词存储。

#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<map>
using namespace std;map<string,int> num;
bool check(char c){//判断字符是否是数字或字母if(c>='0'&&c<='9') return true;if(c>='a'&&c<='z') return true;if(c>='A'&&c<='Z') return true;return false;
}int main(){string s;getline(cin,s);int i=0;   while(i<s.length()){while(i<s.length()&&!check(s[i])){//非数字字母跳过i++;}string word;while(i<s.length()&&check(s[i])){if(s[i]>='A'&&s[i]<='Z'){//小写字母比大写字母多32个字符s[i] +=32;}word+=s[i];i++;}if(word!=""){//单词非空,进行统计map<string,int>::iterator it=num.find(word);if(it!=num.end()) num[word]++;else num[word]=1;}        }string ans;int Max=0;for(map<string,int>::iterator it=num.begin();it!=num.end();it++){if(it->second>Max){Max=it->second;ans=it->first;}}cout<<ans<<" "<<Max;return 0;}

语法:map的遍历采用迭代器,it->first为键值,it->second为映射值。

63. 数字图书馆(1022)

本题要求根据图书的名字、作者、出版社等信息查询满足条件的书号,并顺序输出。核心问题:

(1)读入,字符串间用getline读入每行;(注意,key有多个关键词,需分别读入)
(2)建立字符串信息与学号间的map关系,由于存在一对多的映射,应使用map<string,set<int>>建立映射关系,为便于统一查询处理,年份也用字符串存储。

本题可以不必采用结构体存储信息,因为我们使用结构体很大原因是为了保持同一本书不同信息间的关联性,并且还能够按需求进行排序,但使用map和set都实现这两个需求,并且本题只涉及其余信息与书号间的映射,使用map就够了。

#include<iostream>
#include<stdio.h>
#include<set>
#include<map>
#include<string>
#include<string.h>
using namespace std;map<string,set<int>> mpName,mpAuthor,mpKey,mpPub,mpYear;void query(map<string,set<int>> &mp,string& str){if(mp.find(str)==mp.end()) printf("Not Found\n");else {for(set<int>::iterator it=mp[str].begin();it!=mp[str].end();it++){printf("%07d\n",*it);}}
}int main(){int n,m;int id;string name,author,key,publisher,year;  scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&id);getchar();//getline之间不需要再getchar()getline(cin,name);       getline(cin,author);        while(cin>>key){//key有多个关键字需单独处理mpKey[key].insert(id);char c=getchar();if(c=='\n') break;}        getline(cin,publisher);getline(cin,year);mpName[name].insert(id);mpAuthor[author].insert(id);        mpPub[publisher].insert(id);mpYear[year].insert(id);        }scanf("%d",&m);string temp;int order;for(int i=0;i<m;i++){scanf("%d: ",&order);getline(cin,temp);printf("%d: ",order);cout<<temp<<endl;if(order==1) query(mpName,temp);else if(order==2) query(mpAuthor,temp);else if(order==3) query(mpKey,temp);else if(order==4) query(mpPub,temp);else query(mpYear,temp);}return 0;
}

语法:(1)getline()读入时吸收了回车,因此getline之间不需要再加getline,

(2)对于一行存在空格,以回车结束的字符串,可以用如下形式分别读入中间字符串

while(cin>>key){
            char c=getchar();//吸收空格
            if(c=='\n') break;
        }

(3)map<string,<set>>的用法,插入—mpName[name].insert(id),遍历输出:

for(set<int>::iterator it=mp[str].begin();it!=mp[str].end();it++){
            printf("%07d\n",*it);
        }

65. 出栈序列(1051)

本题要去判断给定序列能否作为出栈序列。根据栈的先进后出性质,以及入队序列的递增性,可以得到如下依据:

(1)若该元素小于上一个出栈元素,说明该元素已入过栈,则直接检查是否与栈顶元素是否相等;
(2)若该元素大于上一个出栈元素,则将入队序列中剩余<=该元素的数字入栈,然后该元素出栈
上述过程不可以爆栈,否则错误。

#include<iostream>
#include<stdio.h>
#include<stack>
using namespace std;int num[8];bool isPop(int m,int n){stack<int> st;int lastPop=-1;//上一个出栈元素int j=1;//当前待入栈元素int stackNum=0;//当前栈内元素for(int i=0;i<n;i++){if(num[i]<lastPop){//小于上一个出栈元素,说明已入过栈if(st.top()!=num[i]){return false;}else{st.pop();//cout<<num[i]<<"出栈"<<endl;lastPop=num[i];stackNum--;}}else{//大于上一个出栈元素,,当前<=num[i]的元素入栈while(j<=num[i]){st.push(j);//cout<<j<<"入栈"<<endl;j++;stackNum++;if(stackNum>m) return false;}st.pop();//cout<<num[i]<<"出栈"<<endl;stackNum--;lastPop=num[i];}}return true;
}int main(){int n,m,k;scanf("%d%d%d",&m,&n,&k);for(int i=0;i<k;i++){for(int j=0;j<n;j++){scanf("%d",&num[j]);            }if(isPop(m,n)){printf("YES\n");}else{printf("NO\n");}}return 0;
}

语法:stack的用法,入栈st.push(),出栈st.pop(),取栈顶元素st.top()

66. 老鼠与大米(1056)

本题要求选出每组重量最大的老鼠进入下一轮,直到选出最终的老鼠。具体实现如下:

(1)根据输入序号序列排序并分组
(2)分组内选出最大数据,进入下一轮,同时对淘汰元素的排名作保存(排名等于剩余元素+1,即当前分组数)。我们是删除原元素,还是把最大值加入新数组呢?
注意到下一轮老鼠也要按之前的先后关系分组,因此可以用队列,先从头遍历出队元素,遍历过程中保存下本组最大质量老鼠,重新入队直到老鼠数量为1
注意:我们把序号入队,这样方便根据序号存储对应的排名,(用重量就需要查找),这也是使用结构体的原因。

#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;struct Mice{int weight,rank;
}mice[1010];queue<int> q;int main(){int Np,Ng;scanf("%d%d",&Np,&Ng);for(int i=0;i<Np;i++){scanf("%d",&mice[i].weight);        }int order;for(int i=0;i<Np;i++){//把序号入队scanf("%d",&order);q.push(order);}while(q.size()!=1){//计算组数int sum=q.size();//sum表示当前队列长度int group =sum/Ng;if(sum%Ng!=0) group++;//分组筛选最大值,最后一组个数可能不同for(int i=0;i<group;i++){int len=Ng;if(i==group-1&&sum%Ng!=0) len=sum%Ng;//最后一组大小可能需要修改int k=q.front();//k存储组内最重老鼠编号for(int j=0;j<len;j++){int now=q.front();if(mice[now].weight>mice[k].weight){k=now;}//先给所有老鼠排名group+1,晋级的老鼠会被之后的正确排名覆盖mice[now].rank=group+1;q.pop();}q.push(k);}        }mice[q.front()].rank=1;//最后一只老鼠是第一名printf("%d",mice[0].rank);for(int i=1;i<Np;i++){        printf(" %d",mice[i].rank);}return 0;
}

语法:队列queue的用法,如入队q.push(k),出队q.pop(),访问首\尾元素q.front()\q.back()。

67. 反转链表(1074)

本题要求按节翻转链表。方法如下:

(1)由于地址都是5位数,可以建立静态链表存储结点数据

(2)按节翻转,用中间变量存储i+2结点地址,把i+1结点指针改成指向i结点,重复该过程。(记得反转一节后,要重新修改上一节末尾元素的指针域)。

注意点:(1)孤立结点不输出,测试点5中输入的结点中存在不在链表上的结点,不应该输出;

(2)注意是按节翻转,不足一节的不翻转,开始没注意到这一点导致测试点1\4错误。

#include<stdio.h>
#include<iostream>
using namespace std;
const int maxn=100010;
struct Node{int data;int next;
}node[maxn];int main(){int head,n,k;scanf("%d%d%d",&head,&n,&k);int add;for(int i=0;i<n;i++){scanf("%d",&add);scanf("%d%d",&node[add].data,&node[add].next);}int head1=head;int num=1;//统计有效个数while(node[head1].next!=-1){num++;head1=node[head1].next;        }int group=num/k;//待翻转组数int j=0;int last,temp,temp2,head2,newhead;while(j<group){       temp=node[head].next;//存储i+1结点temp2;//存储i+2结点head2=head;//备份原来头结点地址for(int i=0;i<k-1;i++){temp2=node[temp].next;//存储i+2结点地址node[temp].next=head;//i+1节点改为指向i结点head=temp;//处理i+1节点temp=temp2;//下一结点改为i+2}if(j>0) node[last].next=head;else newhead=head;last=head2;//本节原头结点地址head = temp;//head移向下一节首结点  j++;}node[last].next=head;int i=0;while(node[newhead].next!=-1&&i<n-1){printf("%05d %d %05d\n",newhead,node[newhead].data,node[newhead].next);newhead=node[newhead].next;i++;}printf("%05d %d %d\n",newhead,node[newhead].data,node[newhead].next);return 0;
}

反思:(1)可以在静态链表结构体内再加上一个地址,方便把有效的结点移动到数组前面统一处理,用下标表示地址虽然节省空间,但限制了灵活性;

(2)由于在翻转过程中head不断移动,常导致头结点丢失,为了保存头结点,导致我的代码存在大量中间变量。有空再回来重新做下这题。

68. 找公共后缀(1032)
本题要求确定两个用链表存储的单词是否存在公共后缀。我用word2的逐个字母与word1比较,看是否存在相同,但这样时间复杂度为O(mn),会在两个测试点超时。

参考解答后,发现可以给每个结点设置一个符号位,单词1经过的字母符号位改为true,再遍历word2一定,一旦发现又flag==true那么就存在相同后缀。

#include<iostream>
#include<stdio.h>
using namespace std;const int maxn=100010;struct Letter{char data;int next;bool flag;
}let[maxn];int main(){int head1,head2,n;scanf("%d%d%d",&head1,&head2,&n);int add;for(int i=0;i<n;i++){scanf("%d",&add);scanf(" %c %d",&let[add].data,&let[add].next);}int pre;for(pre=head1;pre!=-1;pre=let[pre].next){//遍历单词1的所有字母let[pre].flag=true;}for(pre=head2;pre!=-1;pre=let[pre].next){//遍历单词2的所有字母if(let[pre].flag==true){break;}}if(pre!=-1) printf("%05d",pre);else printf("-1");return 0;
}

语法:(1)scanf字符%c时,空格也会被当做字符处理,因此必须根据读入格式加入空格区分,否则会导致读入出错;

(2)bool类型变量默认值为false。

69. 链表排序(1052)

本题要求在把链表按数据大小重新排序。考虑到链表上存在无效结点,因此不能直接按读入顺序存储进结构体数组中,必须先按地址存储,然后再遍历筛选出有效结点(设置序号属性),将有效结点排在前面,再对有效结点排序。

注意:测试点5是0个有效结点的特例,需要特判。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010;struct Node{int address,data,next;int order=maxn;//用于表示链表上的有效结点的序号
}node[maxn];bool cmpValid(Node x,Node y){return x.order<y.order;
}bool cmpData(Node x,Node y){return x.data<y.data;
}int main(){int n,head;scanf("%d%d",&n,&head);int add;for(int i=0;i<n;i++){scanf("%d",&add);scanf("%d%d",&node[add].data,&node[add].next);node[add].address=add;}int cnt=0,pre;for(pre=head;pre!=-1;pre=node[pre].next){node[pre].order=cnt;cnt++;}//把有效结点移动到前面,便于按data排序sort(node,node+maxn,cmpValid);sort(node,node+cnt,cmpData);if(cnt==0) {printf("0 -1");return 0;}printf("%d %05d\n",cnt,node[0].address);for(int i=0;i<cnt-1;i++){printf("%05d %d %05d\n",node[i].address,node[i].data,node[i+1].address);}printf("%05d %d -1\n",node[cnt-1].address,node[cnt-1].data);return 0;
}

70. 链表去重(1097)

本题要求去除绝对值相同的结点,只保留第一个结点。并要求把这些去掉重复结点单独组成一个链表。步骤:
(1)先按静态链表读入,筛选出有效结点。
(2)遍历有效结点,利用辅助数组表示当前数据绝对值是否出现过,出现过的修改序号,通过排序使重复结点排到去重后的有效结点后面输出。

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
const int maxn=100010;
bool num[maxn]={false};//表示该数字的绝对值是否出现struct Node{int add,data,next;int order=2*maxn;
}node[maxn];bool cmpValid(Node x,Node y){return x.order<y.order;
}int main(){int head,n;scanf("%d%d",&head,&n);int address;for(int i=0;i<n;i++){scanf("%d",&address);scanf("%d%d",&node[address].data,&node[address].next);node[address].add=address;}int cnt=0,p;for(p=head;p!=-1;p=node[p].next){node[p].order=cnt++; }    //这里是对整个数组排序,筛选有效结点sort(node,node+maxn,cmpValid);int numSame=0;for(int i=0;i<cnt;i++){//修改绝对值重复元素的序号int absData = abs(node[i].data);if(num[absData]){//该绝对值已经出现过node[i].order = maxn+numSame;numSame++;}else {num[absData]=true;}}//重新排序,重复结点将排到去后链表后面sort(node,node+cnt,cmpValid);        int distinct = cnt-numSame;for(int i=0;i<distinct-1;i++){printf("%05d %d %05d\n",node[i].add,node[i].data,node[i+1].add);}if(distinct>0) printf("%05d %d -1\n",node[distinct-1].add,node[distinct-1].data);for(int i=distinct;i<cnt-1;i++){printf("%05d %d %05d\n",node[i].add,node[i].data,node[i+1].add);}if(numSame>0) printf("%05d %d -1\n",node[cnt-1].add,node[cnt-1].data);return 0;
}

语法:绝对值函数,int用abs(int),double用fab(double),long long用labs(LL)。

71. 整数分解(1103)

本题要求把给定数字N分解成K个数字的P次方之和,如果有多个,选择和最大的,和相同,选择字典序最大的。
(1)确定筛选数的范围,最极端请款,K-1个数是1,那么第K个因子会达到最大因此搜索范围是:(N-K+1)开P次方-1。降序选择是为了优先选出字典序大的。
(2)对这一范围的数,进行深度优先递归(参考算法笔记)。

不过这个字典序排序确实让我迷惑,我加了一段遍历temp和ans比较的代码,但反而出错,不加反而能通过。

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
int N,K,P,maxSum=-1;
int maxFac;//因子的上界
vector<int> temp,ans;//index表示当前选择到index这个数,nowK表示当前已选中因子个数,
void DFS(int index,int nowK,int sum,int Psum){if(nowK==K && Psum==N){//找到这样的K-P分解if(sum>maxSum){maxSum =sum;ans=temp;}/*else if(sum==maxSum){for(int i=0;i<K;i++){if(ans[i]>temp[i]){                    break;}                }}for(int i=0;i<ans.size();i++){printf("%d ",ans[i]);}printf("\n");*/return;}if(index==0 || nowK>K ||Psum>N) return;//选index数据temp.push_back(index);int Psquare =(int)pow((double)index,(double)P);    DFS(index,nowK+1,sum+index,Psum+Psquare);//不选index数据temp.pop_back();DFS(index-1,nowK,sum,Psum);
}int main(){scanf("%d%d%d",&N,&K,&P);maxFac = (int)pow((double)(N-K+1),1.0/P)+1;DFS(maxFac,0,0,0);if(ans.size()==0) {printf("Impossible\n");return 0;}printf("%d =",N);for(int i=0;i<ans.size();i++){printf(" %d^%d",ans[i],P);if(i<ans.size()-1) printf(" +");}return 0;
}

语法:(1)对int型数据求幂,可以采用pow函数,但需要做类型转换,如int Psquare =(int)pow((double)index,(double)P);。

72. 急性脑卒中(1091)

本题要求统计3维0-1矩阵中,由1组成的,个数不小于T的块中,1的总数量。本题与算法笔记上例题类似,采用BFS思想:枚举每一个位置元素,为0跳过,为1,则使用BFS查询相邻的6个位置元素是否为1,若相邻再重复操作,直到访问完整个1块。
辅助数组:(1)记录每个位置是否入过队的inq,减少大量重复计算
(2)增量数组,表示6个方向。

#include<cstdio>
#include<queue>
using namespace std;
struct node{int x,y,z;//位置(x,y,z)
}Node;int n,m,l;//3维矩阵大小
int T;//块下界
int matrix[62][1290][130];
bool inq[62][1290][130]={false};
int X[6]={0,0,0,0,1,-1};
int Y[6]={0,0,1,-1,0,0};
int Z[6]={1,-1,0,0,0,0};bool judge(int x,int y,int z){//判断位置(x,y,z)是否需要访问if(x<0||x>=l || y<0||y>=m || z<0||z>=n) {return false;}if(matrix[x][y][z]==0||inq[x][y][z]==true){return false;}return true;
}int BFS(int x,int y,int z){int total=0;//统计块中1的个数queue<node> Q;Node.x=x,Node.y=y,Node.z=z;Q.push(Node);inq[x][y][z]=true;while(!Q.empty()){node top =Q.front();//取队首元素Q.pop();total++;for(int i=0;i<6;i++){int newX=top.x + X[i];int newY=top.y + Y[i];int newZ=top.z + Z[i];if(judge(newX,newY,newZ)){//新位置需要访问Node.x=newX,Node.y=newY,Node.z=newZ;Q.push(Node);inq[newX][newY][newZ]=true;                }}}//printf("%d\n",total);if(total>=T) return total;else return 0;
}int main(){scanf("%d%d%d%d",&m,&n,&l,&T);    for(int i=0;i<l;i++){for(int j=0;j<m;j++){for(int k=0;k<n;k++){scanf("%d",&matrix[i][j][k]);}}}int volume=0;//块和总体积数//枚举每一位置for(int i=0;i<l;i++){for(int j=0;j<m;j++){for(int k=0;k<n;k++){//若元素为1,且未入过队,则发现新块if(matrix[i][j][k]==1&&inq[i][j][k]==false)volume+=BFS(i,j,k);}}}printf("%d",volume);return 0;
}

73. 树的遍历(1073)

本题要求根据后序序列和中序序列得到层序遍历序列。思路是:
(1)先重建二叉树:从后序序列末尾得到根结点,再由根节点到中序序列中去划分左右子树
对左右子树递归调用上述过程,直到序列长度小于0。
(2)根据二叉树,利用BFS思想,输出层间序列。

重点掌握树的建立和层间序列输出

#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
int post[40],in[40];
int n,num=0;//num表示当前打印数据个数struct BTree{int data;BTree* lchild;BTree* rchild;
};BTree* create(int pl,int pr,int il,int ir){if(pr<pl) return NULL;int inRoot;//中序序列中根节点位置for(int i=il;i<=ir;i++){if(in[i]==post[pr]){inRoot=i;break;}}    BTree* root = new BTree;//建立新结点存放根root->data = post[pr];root->lchild = create(pl,pl+inRoot-il-1,il,inRoot-1);root->rchild = create(pl+inRoot-il,pr-1,inRoot+1,ir);            return root;
}void LayerOrder(BTree* root){queue<BTree*> q;//队列存放地址q.push(root);//根结点入队while(!q.empty()){BTree* now=q.front();//取出队首元素q.pop();printf("%d",now->data);num++;if(num<n) printf(" ");//若左右子树非空则入队if(now->lchild!=NULL) q.push(now->lchild);if(now->rchild!=NULL) q.push(now->rchild);}
}int main(){scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&post[i]);}for(int i=0;i<n;i++){scanf("%d",&in[i]);}//重建树BTree* root = create(0,n-1,0,n-1);LayerOrder(root);return 0;
}

语法:由于队列存放的是原数据的副本,不能直接通过修改队列修改原数据,因此建议队列存储元素的地址,利用地址去修改元素。

73. 树的遍历2(1086)

使用栈可以实现非递归的中序遍历,本题要求根据栈的入栈和出栈操作得到树的后序列。本题的关键在于发现入栈顺序是先序遍历,出栈顺序是中序遍历。

先序:根-左-右,中序:左-根-右。因此,入栈时按先序先入根-左,出栈时就会变成左-根,再入右出右,就实现了先序入栈,中序出栈。因此本题可以这样解决:
(1)先读入操作直接得到先序遍历;
(2)根据操作复现栈,得到出栈的中序序列,
(3)根据先序和中序序列得到后序序列
关于读入问题:可以先scanf一个字符串,如果发现是push,就再读入数字。

注意递归调用时的区间范围。

#include<iostream>
#include<stdio.h>
#include<stack>
#include<string.h>
using namespace std;stack<int> tree;
int pre[32],in[32];
int num=0,n;void postOrder(int pl,int pr,int il,int ir){//根据先序和中序得到后序遍历if(pl>pr) return;int inRoot;for(int i=il;i<=ir;i++){if(in[i]==pre[pl]){inRoot=i;break;}}int len =inRoot-il-1;//对左子树递归postOrder(pl+1,pl+len+1,il,inRoot-1);//对右子树递归postOrder(pl+len+2,pr,inRoot+1,ir);printf("%d",pre[pl]);num++;if(num<n) printf(" ");
}int main(){scanf("%d",&n);char str[5];int t1=0,t2=0;for(int i=0;i<2*n;i++){scanf("%s",str);if(strcmp(str,"Push")==0){//说明是入栈操作scanf("%d",&pre[t1]);tree.push(pre[t1]);t1++;}else{//出栈操作in[t2++]=tree.top();tree.pop();}}postOrder(0,n-1,0,n-1);return 0;
}

75. 反转二叉树(1102)

本题要求根据每个结点的左右子树序号,得到这个树翻转之后的层序遍历和中序遍历结果。
(1)重构这课树,使用静态链表即可,关键是如何确定根结点(根节点是遍历一个树的起始位置)
由于只有根节点不是别的结点的子结点,因此在输入中没有出现的数字就是根节点序号
(2)根据树做层序和中序输出;

#include<stdio.h>
#include<queue>
using namespace std;
const int maxn=100;
int num1=0,num2=0;
int n;
struct Node{//静态链表定义树int lchild,rchild;
}node[maxn];bool num[maxn]={false};//用于确定根节点void LayerOrder(int root){//层序遍历,使用队列,不需要递归queue<int> q;q.push(root);while(!q.empty()){int now=q.front();q.pop();num1++;printf("%d",now);if(num1<n) printf(" ");if(node[now].lchild!=-1) q.push(node[now].lchild);if(node[now].rchild!=-1) q.push(node[now].rchild);    }}void inOrder(int root){if(root==-1) return;inOrder(node[root].lchild);num2++;printf("%d",root);if(num2<n) printf(" ");inOrder(node[root].rchild);
}int main(){scanf("%d",&n);    char c1,c2;for(int i=0;i<n;i++){//把第一、二个序号当左右结点,直接实现翻转scanf("%*c%c %c",&c1,&c2);//%*c把换行吸收//printf("%c %c\n",c1,c2);if(c1=='-'){node[i].rchild=-1;}else{node[i].rchild=c1-'0';num[c1-'0']=true;}if(c2=='-'){node[i].lchild=-1;}else{node[i].lchild=c2-'0';num[c2-'0']=true;}}//寻找根节点int root;for(int i=0;i<n;i++){if(num[i]==false){root=i;break;}}LayerOrder(root);printf("\n");inOrder(root);return 0;
}

语法:scanf读入%c时,会识别换行符,因此需要考虑吸收换行,可以用getchar,也可以用scanf("%*c")的方法。

76. 供应链的总销量 (1079)

本题要求计算所有叶子结点的销售量并求和。叶子结点就是子节点数为0的结点,那么关键就是确定叶子的深度,每个叶子结点是一条从根出发的路径终点,因此这个问题本质上还是一个搜索问题,要求和所有的叶子结点,就是要遍历整棵树,可以用BFS和DFS。(本题默认0号结点为根节点,因此不必寻找根节点)
关键:
(1)读入结点数据,使用静态写法;
(2)从根结点开始用DFS遍历,计算销售量。

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
const int maxn=100010;
int n;
double p,r,sum=0;
struct Node{double sale;vector<int> child;
}node[maxn];//bool num[maxn]={false};void DFS(int root,int layer){if(node[root].child.size()==0){//抵达叶子结点                sum+=node[root].sale*pow(1+r,layer);//printf("layer=%d,root=%d,rate=%.3f,prioty=%.3f\n",layer,root,pow(1+r,layer),prioty);        return;}//孩子结点不为空,则进行递归访问for(int i=0;i<node[root].child.size();i++){DFS(node[root].child[i],layer+1);        }
}int main(){scanf("%d%lf%lf",&n,&p,&r);r/=100;int k,point;for(int i=0;i<n;i++){scanf("%d",&k);if(k==0){scanf("%lf",&node[i].sale);}for(int j=0;j<k;j++){scanf("%d",&point);node[i].child.push_back(point);           }}/*int root;for(int i=0;i<n;i++){if(num[i]==false){root=i;break;}}*/DFS(0,0);printf("%.1f",sum*p);return 0;
}

PAT甲级刷题笔记(4)相关推荐

  1. PAT甲级刷题记录-(AcWing)-(Day06树 8题)

    PAT甲级刷题记录-(AcWing)-(Day06树 8题) 课程来源AcWing 其中AcWing中的题目为翻译好的中文题目 今日刷题列表 1110 Complete Binary Tree 111 ...

  2. 小峰峰的pat甲级刷题记录1020

    小峰峰的pat甲级刷题记录1020 方法一:通过后序和中序序列构建树,再层序输出 #include<iostream> #include<vector> using names ...

  3. 小峰峰的pat甲级刷题记录1030

    很经典的最优路线问题 方法一:dfs 思路: 1.vector数组记录邻居关系用于深搜遍历 2.遍历过程中优化mindis_to[ ] ,fee[ ] 3.遍历过程中记录当前路径path 4.到达终点 ...

  4. Github最强算法刷题笔记.pdf

    资料一 昨晚逛GitHub,无意中看到一位大佬(https://github.com/halfrost)的算法刷题笔记,感觉发现了宝藏!有些小伙伴可能已经发现了,但咱这里还是忍不住安利一波,怕有些小伙 ...

  5. 我收藏的谷歌和阿里大佬的刷题笔记

    金三银四大家在准备校招.社招,或者闲暇的时候,都可以刷刷 Leetcode,保持良好的手感. 之前刷题,一直觉得漫无目的地刷,效率很低.后来发现了两个刷题笔记,谷歌大佬高畅和BAT大佬霜神写的 Lee ...

  6. PAT甲级真题 1018 A+B in Hogwarts--python解法

    PAT甲级真题 1018 A+B in Hogwarts 提交:2638 通过:1559 通过率:59% If you are a fan of Harry Potter, you would kno ...

  7. 三级网络技术刷题笔记

    三级网络技术刷题笔记 RPR与FDDI一样使用双环结构 在RPR环中,源节点向目的节点成功发出的数据帧要由目的节点从环中收回 RPR中每个节点都执行SRP公平算法 RPR环能够在50ms内实现自愈 O ...

  8. 卷进大厂系列之LeetCode刷题笔记:二分查找(简单)

    LeetCode刷题笔记:二分查找(简单) 学算法,刷力扣,加油卷,进大厂! 题目描述 涉及算法 题目解答 学算法,刷力扣,加油卷,进大厂! 题目描述 力扣题目链接 给定一个 n 个元素有序的(升序) ...

  9. 发现一位大佬的算法刷题笔记PDF

    昨晚逛GitHub,无意中看到一位大佬(https://github.com/halfrost)的算法刷题笔记,感觉发现了宝藏!有些小伙伴可能已经发现了,但咱这里还是忍不住安利一波,怕有些小伙伴没有看 ...

最新文章

  1. error LNK2019: 无法解析的外部符号,该符号在函数 _main 中被引用的解决方法
  2. 清华陈文光教授:AI 超算基准测试的最新探索和实践(附演讲视频)
  3. Linux服务器CPU、内存、磁盘空间、负载情况查看python脚本
  4. jmeter mysql 连接复用_JMeter 连接MySQL
  5. 程序员十大心愿,程序员:你这么了解我的心声的嘛!
  6. x64版本的OpenGL库配置
  7. 2数据库表增加一个字段_14个实用的数据库设计技巧!
  8. MySQL 8下忘密码后重置密码的办法(MySQL5老方法不灵了)
  9. python 二分查找函数_Python基础14_递归函数,二分查找
  10. 软件架构及几种典型框架
  11. Read_books_水煮三国
  12. Linux VNC使用
  13. 海康NVR设备上传人脸图片到人脸库
  14. swagger注解介绍
  15. cgroup 分析之CPU和内存部分
  16. 基于SLAM融合构图的自主轮式仓储货运机器人技术说明
  17. AndroidStudio打包AAR供Unity使用流程
  18. 设计模式七大设计原则
  19. 公开预算 publicize budget
  20. 页面设计之论坛网页设计欣赏(前端)

热门文章

  1. ffmpeg合并视频时的尺寸注意事项
  2. ue4 玩家控制器APlayerController
  3. 50台机器无盘服务器,以50台机器小吧为例看深度无盘快速布署的那些事.doc
  4. 设计师:设计师知识储备之室内设计风格图文介绍大全(中式风格、清新风格、现代简约、现代风格、后现代风格、田园风格-中式田园-欧式田园-美式田园-美式乡村风格)之详细攻略
  5. 一文学会招投标数据采集分析
  6. 让数据起飞,MySql索引
  7. 国开计算机应用基础中考答案,国开电大 计算机应用基础(本) 形考一答案
  8. JS中的 || 与 运算符详解
  9. 【LeetCode】999. 车的可用捕获量
  10. codeblocks错误