1. 知识点总结
本次作业没有严格按照试卷题目编排,同时跳过了之前(久远时)做过的题目,这样安排……别问,问就是想要填满空隙的某种不知名强迫症🤣
本次作业涉及到的知识点有
- map+六级
- 二叉树
- 优先队列
- 深度优先搜索
题号 | 难度 | 知识点 |
---|---|---|
1124 | 🐸🐸 | 数组+map,英语😱阅读 |
1125 | 🐸 | 数组,找规律 |
1127 | 🐸🐸 | 二叉树,但是只想给一个半🐸,模板,注意输出STL重排序~ |
1129 | 🐸🐸 | 优先队列+结构体排序(不算难题,但是优先队列忘记怎么处理结构体,实属不应该) |
1130 | 🐸 | 二叉树的中序遍历和括号之间的爱恨情仇 |
1131 | 🐸🐸🐸 | 其实不应该有这个难度,看完题解😱知道是比较基础的DFS变式主要是提醒自己DFS练少了 |
2. 分题题解
2.1 第一题 1124 Raffle for Weibo Followers (20 分)
难点在于……英文叭,没看懂他的意思,感觉不是很难的题目,但是卡在阅读理解上着实难受:
题目的意思,不规定有几个中奖的人,只需要对于输入的人,从初始位置s,每隔着n个算一次中奖,如果有人重复中,s++,跳过这个人
第一遍照着模糊的理解写,暴力12/20,跪了😥,因为以为n是限制,然后m个人是围成一圈😶,错误理解打咩!!!正解在下面
#include<bits/stdc++.h>
using namespace std;
int m,n,s;//m种可能,转的次数,开始地方
vector<string>fid;
map<string,bool>winned;
set<string>st;
int main(){
scanf("%d%d%d",&m,&n,&s);
fid.resize(m);
for(int i=0;i<m;i++){
cin>>fid[i];
winned[fid[i]]=false;
st.insert(fid[i]);
}
if(st.size()<n||s>m){
printf("Keep going...");
}else{
vector<string>ans;
int cnt=0;
s--;//开始的位置
while(cnt!=n){
s%=m;
if(winned[fid[s]]==false){
winned[fid[s]]=true;
ans.push_back(fid[s]);
cnt++;
s+=n;
}else{
s++;
}
}
//输出答案
for(int i=0;i<ans.size();i++){
printf("%s\n",ans[i].c_str());
}
}
return 0;
}
订正之后:
参考柳神代码,简洁度被瞬间秒杀
#include<bits/stdc++.h>
using namespace std;
int m,n,s;//m种可能,转的次数,开始地方
int main(){
scanf("%d%d%d",&m,&n,&s);
string str;
bool flag=false;//是否有人中过奖
map<string,bool>winned;
for(int i=1;i<=m;i++){
cin>>str;
if(winned[str]==true){
s++;
}else if(i==s){
winned[str]=true;
cout<<str<<endl;
flag=true;
s=s+n;
}
}
if(flag==false)printf("Keep going...");
return 0;
}
2.2 第二题 1125 Chain the Ropes (25 分)
找规律题,稳住心态:
先将输入的绳子长度片段按照从小到大排序,sum=(sum+v[i])/2,一直到最后,这样,短的被反复折叠,但是长的安然无恙,可以保证最后结果正确(本来以为是深搜,结果草稿纸上推了一下发现找规律比较简单~)
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>v;
int main(){
scanf("%d",&n);
v.resize(n);
for(int i=0;i<n;i++){
scanf("%d",&v[i]);
}
sort(v.begin(),v.end());
int sum=v[0];
for(int i=1;i<n;i++){
sum=(sum+v[i])/2;
}
printf("%d",sum);
return 0;
}
2.3 第三题 1130 Infix Expression (25 分)
树的中序遍历,括号的输出需要注意,最外层的括号和叶子节点括号不需要输出,其余的在递归过程中输出括号
#include<bits/stdc++.h>
using namespace std;
struct Node{
char data[15];
int left;
int right;
};
vector<Node>nodes;
int n;
bool isLeaf(int a){
if(a==-1)return false;
if(nodes[a].left==-1&&nodes[a].right==-1){
return true;
}
return false;
}
bool isOk(int a){
if(a==-1)return false;
if(isLeaf(nodes[a].left)&&isLeaf(nodes[a].right)){
return true;
}
return false;
}
bool flag=false;
void preTravel(int root,int cnt){
if(root==-1){
return;
}
//bool flag=isOk(root);
if(cnt&&!isLeaf(root)){
printf("(");
}
preTravel(nodes[root].left,cnt+1);
printf("%s",nodes[root].data);
preTravel(nodes[root].right,cnt+1);
if(cnt&&!isLeaf(root)){
printf(")");
}
}
const int maxn=100;
bool isc[maxn]={false};
int main(){
scanf("%d",&n);
nodes.resize(n+1);
for(int i=1;i<=n;i++){
scanf("%s %d %d",nodes[i].data,&nodes[i].left,&nodes[i].right);
isc[nodes[i].left]=true;
isc[nodes[i].right]=true;
}
//找到根节点
int root=-1;
for(int i=1;i<=n;i++){
if(!isc[i]){
root=i;
break;
}
}
preTravel(root,0);
return 0;
}
2.4 第四题 1129 Recommendation System (25 分)
直觉告诉我应该用优先队列,然鹅……忘记优先队列怎么用了,翻看了API……罪过罪过
优先队列+结构体
每次输出的时候还得将不同id存到vector中,输出后再存回到优先队列中
#include<bits/stdc++.h>
using namespace std;
int n,k;
struct Node{
int id;
int t;
};
bool operator<(Node a,Node b){
if(a.t!=b.t){
return a.t<b.t;
}else{
return a.id>b.id;
}
}
priority_queue<Node>ans;
const int maxn=50009;
int cnt[maxn]={0};
Node temp;
int id;
vector<Node>op;
set<int>st;
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&id);
cnt[id]++;
if(i!=1){
printf("%d:",id);
int output=k<st.size()?k:st.size();
//输出
for(int x=0;x<output;x++){
temp=ans.top();
ans.pop();
printf(" %d",temp.id);
if(temp.id!=id){
op.push_back(temp);
}
}
//存回去
for(int x=0;x<op.size();x++){
ans.push(op[x]);
}
op.clear();
printf("\n");
}
temp.id=id;
temp.t=cnt[id];
ans.push(temp);
st.insert(id);
}
return 0;
}
2.5 第五题 1127 ZigZagging on a Tree (30 分)
感觉PTA一般考二叉树变式比较多,但是Dij、并查集、优先队列还是得熟练掌握,不然真的白给😂,(貌似、也许、好像其他机试不会这么痴迷于树的~
- 中序遍历+后序遍历**—>**层序遍历输出结果
- 结构体存储层次遍历输出的结果,重新排序后按照要求输出
#include<bits/stdc++.h>
using namespace std;
int N;
vector<int>post;
vector<int>in;
struct Node{
int val;
Node *left;
Node *right;
int level;
};
struct Ans{
int data;
int level;
int cnt;
};
vector<Ans>ans;
bool cmp(Ans a,Ans b){
if(a.level!=b.level){
return a.level<b.level;
}else{
if(a.level%2==1){
//单数层,从右往左
return a.cnt>b.cnt;
}else{
return a.cnt<b.cnt;
}
}
}
Node *build(int inL,int inR,int postL,int postR){
if(inL>inR){
return NULL;
}
Node *root=new Node;
root->val=post[postR];
int k;
for(k=inL;k<=inR;k++){
if(in[k]==post[postR]){
break;
}
}
int left_num=k-inL;
root->left=build(inL,k-1,postL,postL+left_num-1);
root->right=build(k+1,inR,postL+left_num,postR-1);
return root;
}
void levelTravel(Node *root){
queue<Node*>q;
root->level=1;
q.push(root);
bool flag=false;
Node *temp;
int cnt=0;
while(!q.empty()){
cnt++;
Node *top=q.front();
q.pop();
if(top->left!=NULL){
temp=top->left;
temp->level=top->level+1;
q.push(temp);
}
if(top->right!=NULL){
temp=top->right;
temp->level=top->level+1;
q.push(temp);
}
//保存每一行的
Ans a;
a.level=top->level;
a.data=top->val;
a.cnt=cnt;
ans.push_back(a);
}
return ;
}
int main(){
scanf("%d",&N);
post.resize(N);
in.resize(N);
for(int i=0;i<N;i++){
scanf("%d",&in[i]);
}
for(int i=0;i<N;i++){
scanf("%d",&post[i]);
}
Node*root=build(0,N-1,0,N-1);
levelTravel(root);
sort(ans.begin(),ans.end(),cmp);
for(int i=0;i<N;i++){
if(i)printf(" ");
printf("%d",ans[i].data);
}
return 0;
}
2.6 第六题 1131 Subway Map (30 分)
碎碎念:感觉考搜索不是很多,难度的话,只要读懂题目,一般不是很卡时间(😁)~
因为花了一个下午才勉强上19……喵呜呜呜,在面壁了,所以直接看了博客的答案,柳神给的思路还是深搜……(标准简介版),有大佬说DFS在数据严格下其实是不可以的,PTA测试数据点没有非常严格,所以一般的DFS可以过
订正思路和注意点贴在第一版下面~😥
第一版:19/30
DFS感觉手生呜呜呜
#include<bits/stdc++.h>
using namespace std;
# define INF 99999999
struct Node{
int sid;
int line;
};
int n,k,num,sid;
int sign;
int vis[10000]={0};
vector<Node>taken;
vector<Node>final;//最终答案
vector<vector<int> >lines;//每条线路经过的站点名称
map<int,vector<int> >stop; //每个站点属于哪个路线
//计算最少的站台
int st,ed;
int ans;
int changes;
int getNextSid(int pos,int line){
int len=lines[line].size();
for(int i=0;i<len;i++){
if(lines[line][i]==pos){
if(i==len-1){
return -1;
}else{
return lines[line][i+1];
}
}
}
return -1;
}
int getPreSid(int pos,int line){
int len=lines[line].size();
for(int i=1;i<len;i++){
if(lines[line][i]==pos){
return lines[line][i-1];
}
}
return -1;
}
void dfs(int pos,int line,int gone,int cnt){
/*当前站台编号,线路,已经经过的站点个数*/
//答案不唯一的话,输出转乘最小的
//printf("当前站点%04d, 当前路线:%d ,当前经过站点个数%d\n",pos,line,gone);
gone++;
vis[pos]=sign;
Node node;
node.line=line;
node.sid=pos;
taken.push_back(node);
if(gone>ans||(gone==ans&&cnt>changes)){
return;
}
//结束,不需要继续探索
if((pos==ed&&ans>gone)||(pos==ed&&ans==gone&&cnt<changes)){
ans=gone;
final=taken;
return;
}
//探索
for(int i=0;i<stop[pos].size();i++){
int tline=stop[pos][i];
//下一站:
int nextpos=getNextSid(pos,tline);
int nc=cnt;
if(tline!=line)nc++;
if(nextpos!=-1&&vis[nextpos]!=sign){
dfs(nextpos,tline,gone,nc);
//回溯
taken.pop_back();
vis[nextpos]=sign-1;
}
//上一站:
int prepos=getPreSid(pos,tline);
if(prepos!=-1&&vis[prepos]!=sign){
dfs(prepos,tline,gone,nc);
//回溯
taken.pop_back();
vis[prepos]=sign-1;
}
}
return;
}
int main(){
scanf("%d",&n);
lines.resize(n);
for(int i=0;i<n;i++){
scanf("%d",&num);
for(int j=0;j<num;j++){
scanf("%d",&sid);
lines[i].push_back(sid);
stop[sid].push_back(i);
}
}
scanf("%d",&k);
for(int i=1;i<=k;i++){
sign=i;
taken.clear();
scanf("%d%d",&st,&ed);
ans=INF;
changes=INF;
for(int j=0;j<stop[st].size();j++){
dfs(st,stop[st][j],0,0);
}
//输出答案
printf("%d\n",ans-1);
bool flag1=false,flag2=false;//起点、终点是否输出过
int s=final[0].sid,e=final[0].sid,myline=final[0].line;
for(int x=0;x<final.size();x++){
if(final[x].line==myline){
e=final[x].sid;
}else{
//不一样
printf("Take Line#%d from %04d to %04d.\n",myline+1,s,e);
s=final[x-1].sid,e=final[x].sid,myline=final[x].line;
}
}
printf("Take Line#%d from %04d to %04d.\n",myline+1,s,e);
}
return 0;
}
DFS版本:
忽略Line路线的影响,先当作图论中的寻找最短路径问题,深搜展开,最后判断的时候先比较路径长度,长度一致的情况下比较线路站次数。
在传统的dfs求最短路径上,加上了
lines【i】【j】
数组存储某路段属于哪一个路线Line(因为题目中明确说没有重合的路段,只有重合的站点,再次反映审题的重要性!!!!)
- 图论(DFS最短路径)
- STL存储答案
- 最后路线长度相同时,
transCount(temppath)
函数另外计算转站次数~
第二版:AC
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000;
const int inf=99999999;
vector<vector<int> >graph(maxn);//存储图_站点之间的连接关系
int lines[maxn][maxn];
bool vis[maxn]={false};
vector<int>path,temppath;
int n,k,num,sid;
int st,ed,pre;
int minCnt,minTrans;
int transCount(vector<int>temppath){
int len=temppath.size();
if(len<=2)return 0;
int sum=0;
int pre=temppath[0],sid=temppath[1];
int preLine=lines[pre][sid];
for(int i=2;i<len;i++){
pre=temppath[i-1];
sid=temppath[i];
if(lines[pre][sid]!=preLine){
sum++;
preLine=lines[pre][sid];
}
}
return sum;
}
void dfs(int pos,int cnt) {
//当前车站号码,走过的站数
if(pos==ed&&((cnt<minCnt)||(cnt==minCnt&&transCount(temppath)<minTrans))){
//符合条件
// printf("pos==%d\n",pos);
// printf("debug:%d %d real:%d %d\n",temppath[0],temppath[temppath.size()-1],st,ed);
minCnt=cnt;
minTrans=transCount(temppath);
path=temppath;
return;
}
if(pos==ed||cnt>minCnt){
return;
}
//深搜
for(int i=0;i<graph[pos].size();i++){
int newpos=graph[pos][i];
if(vis[newpos]==false){
vis[newpos]=true;
temppath.push_back(newpos);
dfs(newpos,cnt+1);
//回溯
vis[newpos]=false;
temppath.pop_back();
}
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d%d",&num,&pre);
for(int j=1;j<num;j++){
scanf("%d",&sid);
graph[pre].push_back(sid);
graph[sid].push_back(pre);
lines[sid][pre]=i;
lines[pre][sid]=i;
pre=sid;
}
}
scanf("%d",&k);
for(int i=0;i<k;i++){
scanf("%d%d",&st,&ed);
minCnt=inf;
minTrans=inf;
temppath.clear();
temppath.push_back(st);
vis[st]=true;
dfs(st,0);
vis[st]=false;
printf("%d\n",minCnt);
//输出答案
int s=st,len=path.size();
int preLine=lines[s][path[1]];
for(int j=1;j<len;j++){
if(lines[path[j-1]][path[j]]!=preLine){
printf("Take Line#%d from %04d to %04d.\n",preLine+1,s,path[j-1]);
s=path[j-1];
preLine=lines[path[j-1]][path[j]];
}
}
//最后输出
printf("Take Line#%d from %04d to %04d.\n",lines[path[len-2]][ed]+1,s,ed);
// for(int j=1;j<len;j++){
// printf("line#%d: %04d->%04d\n",lines[path[j-1]][path[j]],path[j-1],path[j]);
// }
}
return 0;
}
挖个坑,我觉得Dij也是可以的~