终于考完了qaq把最后一堆也整理出来了
目录
1121 Damn Single(25)set、map的使用
1122 Hamiltonian Cycle(25)哈密顿回路
1123 Is It a Complete AVL Tree(30)AVL树+bfs
1124 Raffle for Weibo Followers(20)set的使用
1126 Eulerian Path(25)欧拉路径+连通图
1129 Recommendation System(25)set的使用
1133 Splitting A Linked List(25)链表
1135 Is It A Red-Black Tree(30)红黑树
1136 A Delayed Palindrome(20)回文串+模拟
1138 Postorder Traversal(25)前序、中序转后序
1140 Look-and-say Sequence(20)字符串处理
1141 PAT Ranking of Institutions(25)排序+map
1143 Lowest Common Ancestor(30)BST树+模拟
1144 The Missing Number(20)set的使用
1145 Hashing – Average Search Time(25)二次方探查法
1146 Topological Order(25)拓扑排序
1148 Werewolf – Simple Version(20)暴力模拟
1151 LCA in a Binary Tree(30)LCA+中序、先序转换
1121 Damn Single
(25)set、map的使用
【题意】
给出n对夫妻,有q次查询,若夫妻都在这q次查询中,说明不是单身狗,如果查询的人没有对象或者对象不在查询中则是单身狗,输出有多少单身狗,并输出单身狗的ID。
【解题思路】
将夫妻的ID都用节点记录下来,并用map存储他们的编号,然后用b数组记录在q次查询中是否出现相应序号的对象,如果出现两次,则说明夫妻都在这q次查询中,则将这对夫妻存入set,然后再遍历这q次查询,将set中没有出现过的人的序号存入s2,最后输出s2的大小并且遍历s2即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=50005;
int a[maxn],b[maxn];
struct Node
{
int x,y;
}node[maxn];
int main()
{
int n,flag=0;
scanf("%d",&n);
map<int,int>m;
set<int>s,s2;
set<int>::iterator it;
for(int i=0;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
node[i].x=x;node[i].y=y;
m[x]=m[y]=i;
}
int q;
scanf("%d",&q);
for(int i=0;i<q;i++)
{
scanf("%d",&a[i]);
if(m.count(a[i]))b[m[a[i]]]++;
if(b[m[a[i]]]==2)
{
s.insert(node[m[a[i]]].x);
s.insert(node[m[a[i]]].y);
}
}
for(int i=0;i<q;i++)
{
if(!s.count(a[i]))s2.insert(a[i]);
}
printf("%d\n",s2.size());
if(s2.size()>0)
{
for(it=s2.begin();it!=s2.end();it++)
{
(it==s2.begin())?printf("%05d",*it):printf(" %05d",*it);
}
printf("\n");
}
return 0;
}
1122 Hamiltonian Cycle
(25)哈密顿回路
【题意】
给出一幅图,并给出一条路径,判断是否为哈密顿回路(哈密顿回路即除第一个和最后一个顶点外每个顶点经过一次并构成回路)。
【解题思路】
本题哈密顿回路所需的条件:
1.第一个顶点与最后一个顶点相同
2.顶点个数等于n+1
3.除最后一个顶点外的其他顶点不能有重复
4.每两个顶点之间要有路径
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
int edge[maxn][maxn],a[maxn];
int main()
{
int n,m,q;
scanf("%d%d",&n,&m);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
edge[u][v]=edge[v][u]=1;
}
scanf("%d",&q);
while(q--)
{
int t,flag=1;
set<int>s;
scanf("%d",&t);
for(int i=0;i<t;i++)
{
scanf("%d",&a[i]);
if(s.count(a[i]) && i!=t-1)
flag=0;
s.insert(a[i]);
}
if(t<=1 ||!edge[a[1]][a[0]])flag=0;
for(int i=1;i<t;i++)
{
if(!edge[a[i]][a[i-1]])
flag=0;
}
if(flag && t==n+1 && a[0]==a[t-1])printf("YES\n");
else printf("NO\n");
}
return 0;
}
1123 Is It a Complete AVL Tree
(30)AVL树+bfs
【题意】
将AVL树层序输出,并判断该AVL树是否为完全二叉树。
【解题思路】
先套个AVL模板建树,然后根据bfs判断是否为二叉树,即设置一个cnt计数,当节点为空且cnt=n时说明是完全二叉树,如果节点为空但是cnt!=n说明后面还有不为空的结点,所以是不完全二叉树。
【代码】
#include<bits/stdc++.h>
using namespace std;
int a[25],cnt=0,flag=1;
typedef struct Node
{
int val;
struct Node *left,*right;
}Node,*ANode;
int getHeight(ANode root)
{
if(root==NULL)return 0;
return max(getHeight(root->left),getHeight(root->right))+1;
}
ANode llrotatio(ANode root)
{
ANode k1;
k1=root->left;
root->left=k1->right;
k1->right=root;
return k1;
}
ANode rrrotation(ANode root)
{
ANode k1;
k1=root->right;
root->right=k1->left;
k1->left=root;
return k1;
}
ANode lrrotation(ANode root)
{
root->left=rrrotation(root->left);
return llrotatio(root);
}
ANode rlrotation(ANode root)
{
root->right=llrotatio(root->right);
return rrrotation(root);
}
ANode insertnode(ANode root,int x)
{
if(root==NULL)
{
root=new Node();
root->left=root->right=NULL;
root->val=x;
}
else if(x<root->val)
{
root->left=insertnode(root->left,x);
if(getHeight(root->left)-getHeight(root->right)==2)
root=(x<root->left->val)?llrotatio(root):lrrotation(root);
}
else if(x>root->val)
{
root->right=insertnode(root->right,x);
if(getHeight(root->right)-getHeight(root->left)==2)
root=(x<root->right->val)?rlrotation(root):rrrotation(root);
}
return root;
}
void preorder(ANode root)
{
if(root==NULL)return;
printf("%d ",root->val);
preorder(root->left);
preorder(root->right);
}
void bfs(ANode root,int n)
{
queue<ANode>q;
q.push(root);
while(!q.empty())
{
ANode t=q.front();
q.pop();
if(t==NULL)
{
if(cnt!=n)flag=0;
}
else a[cnt++]=t->val;
if(t!=NULL)q.push(t->left);
if(t!=NULL)q.push(t->right);
}
}
int main()
{
ANode root=NULL;
int n,x;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&x);
root=insertnode(root,x);
}
bfs(root,n);
printf("%d",a[0]);
for(int i=1;i<cnt;i++)
printf(" %d",a[i]);
if(!flag)printf("\nNO\n");
else printf("\nYES\n");
return 0;
}
1124 Raffle for Weibo Followers
(20)set的使用
【题意】
小明PAT考了满分,高兴之余决定发起微博转发抽奖活动,从转发的网友中按顺序每隔N个人就发出一个红包。请你编写程序帮助他确定中奖名单。注意:可能有人转发多次,但不能中奖多次。所以如果处于当前中奖位置的网友已经中过奖,则跳过他顺次取下一位。按照输入的顺序输出中奖名单,每个昵称占一行。如果没有人中奖,则输出“Keep going…”
【解题思路】
用set存储已经中奖的人,因为最后要按输入的顺序输出中奖名单,所以不能用set存储会自动排序,所以还是需要定义一个string数组来存储,当set的大小为0时说明没有人中奖输出keep going。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
string s[maxn],ans[maxn];
int main()
{
int n,m,cnt=0,q;
set<string>t;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
cin>>s[i];
int i=q;
while(i<=n)
{
if(!t.count(s[i]))
{
t.insert(s[i]);
ans[cnt++]=s[i];
}
else
{
while(t.count(s[i]) && i<=n)
i++;
ans[cnt++]=s[i];
}
i+=m;
}
if(t.size()==0)printf("Keep going...\n");
else
{
for(i=0;i<cnt;i++)
cout<<ans[i]<<endl;
}
return 0;
}
1125 Chain the Ropes
(25)排序
【题意】
有n段绳子要将他们连在一起,每两段绳子连在一起后他们的长度就会减半,输出不超过最大长度的最接近的整数。
【解题思路】
先从小到大排个序,因为要保证长度大的绳子被减半的次数少,所以只要从小到大计算即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
double a[maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lf",&a[i]);
sort(a,a+n);
double x=(a[0]+a[1])/2;
for(int i=2;i<n;i++)
x=(x+a[i])/2;
int ans=x;
printf("%d\n",ans);
return 0;
}
1126 Eulerian Path
(25)欧拉路径+连通图
【题意】
如果一个连通图的所有结点的度都是偶数,那么它就是Eulerian,如果除了两个结点的度是奇数其他都是偶数,那么它就是Semi-Eulerian,否则就是Non-Eulerian。
【解题思路】
无向图判断欧拉回路:所有顶点的度数为偶数。
欧拉路径:有两个顶点的度数为奇数,其余顶点为偶数。
但是这题有个坑点就是首先得保证这是一个连通图,所以先用dfs或并查集判断一下,如果不是连通图则直接输出Non-Eulerian,如果是再进行接下来的判断。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
int degree[maxn],vis[maxn],num=0;
vector<int>v[maxn];
void dfs(int x)
{
vis[x]=1;
num++;
for(int i=0;i<v[x].size();i++)
{
int t=v[x][i];
if(!vis[t])
dfs(t);
}
}
int main()
{
int n,m,cnt=0;
scanf("%d%d",&n,&m);
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
degree[a]++;
degree[b]++;
}
for(int i=1;i<=n;i++)
(i==1)?printf("%d",degree[i]):printf(" %d",degree[i]);
dfs(1);
if(num!=n)
{
printf("\nNon-Eulerian\n");
return 0;
}
for(int i=1;i<=n;i++)
{
if(degree[i]%2)cnt++;
}
if(cnt==2)printf("\nSemi-Eulerian\n");
else if(cnt==0)printf("\nEulerian\n");
else printf("\nNon-Eulerian\n");
return 0;
}
1128 N Queens Puzzle
(20)模拟
【题意】
n皇后问题,n皇后放在n*n的棋盘中,要求任意一行、一列、对角线上都不能有皇后。题中给出了n个皇后在第i列的位置,判断是否可以使他们互不攻击。
【解题思路】
因为题中保证他们不在同一列,所以只要判断他们是否在同一行或者在同一对角线上即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int a[maxn];
int main()
{
int n,T;
scanf("%d",&T);
while(T--)
{
int flag=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=j)
{
if(a[i]==a[j] || fabs(i-j)==fabs(a[i]-a[j]))
{
flag=1;
break;
}
}
}
}
if(flag)printf("NO\n");
else printf("YES\n");
}
return 0;
}
1129 Recommendation System
(25)set的使用
【题意】
根据用户每次点击的东西的编号,输出他在点当前编号之前应该给这个用户推荐的商品的编号,只推荐k个,也就是输出用户曾经点击过的商品编号的最多的前k个,如果恰好两个商品有相同的点击次数,就输出编号较小的那个。
【解题思路】
首先在struct node里面重载<号,如果当前num不等于a.num就将num大的排在前,否则将key小的排在前面~
每次输入的时候,先不插入,先输出,当i != 0时候开始输出,因为i==0时候用户才第一次点击,没有可以推荐的,输出的同时记录输出过的个数cnt,当cnt< k的时候输出,因为只要前k个~所以就从头到尾依次输出set中的值就可以啦~
book[num]标记num出现的次数,每次寻找set中当前值为num和次数为f[num]的那个值,如果找到了就把他移除,(找到说明这个数已经出现过啦,cnt已经不对啦,先移除掉吧)然后将f[num]+1,在将node(num, book[num])插入到set中,set会帮忙根据我们自定义的<的规则自动排序哒。
来自柳神:
https://www.liuchuo.net/archives/3848
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
int f[maxn];
struct Node
{
int key,num;
bool operator <(const Node &a)const
{
return (num!=a.num)?num>a.num:key<a.key;
}
};
int main()
{
int n,k;
scanf("%d%d",&n,&k);
set<Node>s;
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
if(i!=0)
{
int cnt=0;
printf("%d:",x);
for(set<Node>::iterator it=s.begin();it!=s.end() && cnt<k;it++)
{
printf(" %d",it->key);
cnt++;
}
printf("\n");
}
set<Node>::iterator it=s.find(Node{x,f[x]});
if(it!=s.end())s.erase(it);
f[x]++;
s.insert(Node{x,f[x]});
}
return 0;
}
1130 Infix Expression
(25)dfs
【题意】
给一个二叉树,输出中缀表达式,且加上括号表示运算的优先级。
【解题思路】
即中序遍历,叶子节点和根节点处不需要加(),遍历到其他节点时需要加()。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=25;
int f[25],root;
struct Node
{
string val;
int left=-1,right=-1;
}node[maxn];
void dfs(int x)
{
if(node[x].left==-1 && node[x].right==-1)
{
cout<<node[x].val;
return;
}
if(x!=root)printf("(");
if(node[x].left!=-1)
dfs(node[x].left);
cout<<node[x].val;
if(node[x].right!=-1)
dfs(node[x].right);
if(x!=root)printf(")");
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>node[i].val>>node[i].left>>node[i].right;
if(node[i].left!=-1)f[node[i].left]=1;
if(node[i].right!=-1)f[node[i].right]=1;
}
for(int i=1;i<=n;i++)
{
if(!f[i])
{
root=i;
break;
}
}
dfs(root);
printf("\n");
return 0;
}
1132 Cut Integer
(20)模拟
【题意】
给一个偶数个位的正整数num,把它从中间分成左右两个整数a、b,问num能不能被a和b的乘积整除,能的话输出yes,不能的话输出no。
【解题思路】
输入用字符串比较方便,然后需要注意当sum1*sum2=0时是不能取余的,否则会出现浮点错误。
【代码】
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
char s[15];
int sum=0,sum1=0,sum2=0;
scanf("%s",s);
for(int i=0;i<strlen(s);i++)
sum=sum*10+s[i]-'0';
for(int i=0;i<strlen(s)/2;i++)
sum1=sum1*10+s[i]-'0';
for(int i=strlen(s)/2;i<strlen(s);i++)
sum2=sum2*10+s[i]-'0';
if(sum1*sum2!=0 && sum%(sum1*sum2)==0)
printf("Yes\n");
else printf("No\n");
}
return 0;
}
1133 Splitting A Linked List
(25)链表
【题意】
给一个链表和K,遍历链表后将<0的结点先输出,再将0~k区间的结点输出,最后输出>k的结点。
【解题思路】
将链表先遍历一遍,因为有可能会有节点不在链表中,然后分成3段,data<0,0<=data<=k,data>k即可,最后输出时最后一个节点的下个地址是-1。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct Node
{
int add,data,next;
}node[maxn];
int main()
{
int start,n,k;
scanf("%d%d%d",&start,&n,&k);
while(n--)
{
int add,data,next;
scanf("%d%d%d",&add,&data,&next);
node[add].add=add;
node[add].data=data;
node[add].next=next;
}
vector<int>v,ans;
while(start!=-1)
{
v.push_back(node[start].add);
start=node[start].next;
}
for(int i=0;i<v.size();i++)
if(node[v[i]].data<0)ans.push_back(v[i]);
for(int i=0;i<v.size();i++)
if(node[v[i]].data>=0 && node[v[i]].data<=k)ans.push_back(v[i]);
for(int i=0;i<v.size();i++)
if(node[v[i]].data>k)ans.push_back(v[i]);
for(int i=0;i<ans.size()-1;i++)
printf("%05d %d %05d\n",ans[i],node[ans[i]].data,ans[i+1]);
printf("%05d %d -1\n",ans[ans.size()-1],node[ans[ans.size()-1]].data);
return 0;
}
1134 Vertex Cover
(25)模拟
【题意】
给n个结点m条边,再给k个集合。对这k个集合逐个进行判断。每个集合S里面的数字都是结点编号,求问整个图所有的m条边两端的结点,是否至少一个结点出自集合S中。如果是,输出Yes否则输出No。
【解题思路】
将每条边储存到结构中,然后用set储存需要查询的集合,再遍历每一条边,当存在一条边它的两个顶点都不在set中时终止遍历,输出No,反之输出Yes。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
struct Node
{
int u,v;
}node[maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
scanf("%d%d",&node[i].u,&node[i].v);
int k;
scanf("%d",&k);
while(k--)
{
int p,flag=1;
set<int>s;
scanf("%d",&p);
while(p--)
{
int x;
scanf("%d",&x);
s.insert(x);
}
for(int i=0;i<m;i++)
{
if(!s.count(node[i].u) && !s.count(node[i].v))
{
flag=0;
break;
}
}
if(flag)printf("Yes\n");
else printf("No\n");
}
return 0;
}
1135 Is It A Red-Black Tree
(30)红黑树
【题意】
给一棵二叉搜索树的前序遍历,判断它是否为红黑树,是输出Yes,否则输出No。
【解题思路】
1.根结点是否为黑色
2.如果一个结点是红色,它的孩子节点是否都为黑色
3.从任意结点到叶子结点的路径中,黑色结点的个数是否相同
所以分为以下几步:
0. 根据先序建立一棵树,用链表表示
1. 判断根结点(题目所给先序的第一个点即根结点)是否是黑色【arr[0] < 0】
2. 根据建立的树,从根结点开始遍历,如果当前结点是红色,判断它的孩子节点是否为黑色,递归返回结果【judge函数】
3. 从根节点开始,递归遍历,检查每个结点的左子树的高度和右子树的高度(这里的高度指黑色结点的个数),比较左右孩子高度是否相等,递归返回结果【judge2函数】
参考柳神:
https://www.liuchuo.net/archives/4099
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=35;
int a[maxn];
typedef struct Node
{
int val;
struct Node *left,*right;
}Node,*RBNode;
RBNode insertnode(RBNode root,int x)
{
if(root==NULL)
{
root=new Node();
root->val=x;
root->left=root->right=NULL;
}
else if(abs(x)<abs(root->val))
root->left=insertnode(root->left,x);
else
root->right=insertnode(root->right,x);
return root;
}
int judge(RBNode root)
{
if(root==NULL)return 1;
if(root->val<0)
{
if(root->left!=NULL && root->left->val<0)return 0;
if(root->right!=NULL &&root->right->val<0)return 0;
}
return judge(root->left) && judge(root->right);
}
int getNum(RBNode root)
{
if(root==NULL)return 0;
int l=getNum(root->left);
int r=getNum(root->right);
return root->val>0?max(l,r)+1:max(l,r);
}
int judge2(RBNode root)
{
if(root==NULL)return 1;
if(getNum(root->left)!=getNum(root->right))return 0;
return judge2(root->left)&& judge2(root->right);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
RBNode root=NULL;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
root=insertnode(root,a[i]);
}
if(a[0]<0 || !judge(root) ||!judge2(root))
printf("No\n");
else printf("Yes\n");
}
return 0;
}
1136 A Delayed Palindrome
(20)回文串+模拟
【题意】
判断一个数是不是回文串,如果不是的话加上它逆序后的数,判断是否是回文串,如果不是继续上述操作,当操作数大于10时输出Not found in 10 iterations。
【解题思路】
主要是字符串模拟大数相加过程,感觉用string来做比较方便,别忘了进位即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
int judge(string s)
{
for(int i=0;i<s.size()/2;i++)
{
if(s[i]!=s[s.size()-i-1])
return 0;
}
return 1;
}
int main()
{
string s,s1,s2;
int k=0,t=0,flag=0;
cin>>s;
if(judge(s))
{
cout<<s<<" is a palindromic number."<<endl;
return 0;
}
while(k<10)
{
s1=s;
reverse(s.begin(),s.end());
s2=s;
s="";
t=0;
for(int i=s1.size()-1;i>=0;i--)
{
char c=(s1[i]-'0'+s2[i]-'0'+t)%10+'0';
t=(s1[i]-'0'+s2[i]-'0'+t)/10;
s=c+s;
}
if(t!=0)
{
char c=t+'0';
s=c+s;
}
cout<<s1<<" + "<<s2<<" = "<<s<<endl;
if(judge(s))
{
cout<<s<<" is a palindromic number."<<endl;
flag=1;
break;
}
k++;
}
if(!flag)printf("Not found in 10 iterations.\n");
return 0;
}
1137 Final Grading
(25)排序+map
【题意】
当Gp成绩>=200且最终成绩>=60 && <100的同学能获得证书,最终成绩的计算方法是:当期中成绩大于期末成绩时,最终成绩=期中*0.4+期末*0.6(四舍五入),反之,最终成绩=期末成绩,没有成绩的位置用-1标出,最后输出能够拿到证书的同学的信息,按最终成绩排序,若最终成绩相等,则按照名字的字典序排序。
【解题思路】
按照题意将每个人的信息存入结构中,为了方便用map存储每个人的下标,也可以更方便地查找这个人的信息是否已经存在,然后按照题意排序最后输出即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
struct Node
{
string name;
int gp=-1,gm=-1,gf=-1,g=-1;
}node[maxn];
bool cmp(Node a,Node b)
{
return a.g==b.g?a.name<b.name:a.g>b.g;
}
int main()
{
int p,m,n,cnt=0;
map<string,int>ma;
scanf("%d%d%d",&p,&m,&n);
while(p--)
{
string s;
int x;
cin>>s>>x;
if(x<200)continue;
if(!ma.count(s))ma[s]=cnt++;
node[ma[s]].name=s;
node[ma[s]].gp=x;
}
while(m--)
{
string s;
int x;
cin>>s>>x;
if(!ma.count(s))continue;
node[ma[s]].name=s;
node[ma[s]].gm=x;
}
while(n--)
{
string s;
int x;
cin>>s>>x;
if(!ma.count(s))continue;
node[ma[s]].name=s;
node[ma[s]].gf=x;
}
for(int i=0;i<cnt;i++)
{
if(node[i].gm>node[i].gf)
node[i].g=round(node[i].gm*0.4+node[i].gf*0.6);
else node[i].g=node[i].gf;
}
sort(node,node+cnt,cmp);
for(int i=0;i<cnt;i++)
{
if(node[i].g>=60 && node[i].g<=100)
cout<<node[i].name<<" "<<node[i].gp<<" "<<node[i].gm<<" "<<node[i].gf<<" "<<node[i].g<<endl;
}
return 0;
}
1138 Postorder Traversal
(25)前序、中序转后序
【题意】
给定一棵树的前序序列和中序序列,求树的后序序列的第一个数。
【解题思路】
root为当前子树的根结点在前序pre中的下标,left和rigtht为当前子树的最左边和最右边的结点在中序in中的下标。用i找到当前子树的根结点root在中序中的下标,然后左边和右边就分别为当前根结点root的左子树和右子树,然后递归实现。因为本题只需要求后序序列的第一个数,所以只要v中有元素就直接可以退出搜索。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
int pre[maxn],in[maxn],n;
vector<int>v;
void dfs(int root,int left,int right)
{
if(left>right || v.size()!=0)return;
int i=left;
while(pre[root]!=in[i])i++;
if(i>n)return;
dfs(root+1,left,i-1);
dfs(root+1+i-left,i+1,right);
v.push_back(pre[root]);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&pre[i]);
for(int i=0;i<n;i++)
scanf("%d",&in[i]);
dfs(0,0,n-1);
printf("%d\n",v[0]);
return 0;
}
1140 Look-and-say Sequence
(20)字符串处理
【题意】
给两个数字D和n,第一个序列是D,后一个序列描述前一个序列的所有数字以及这个数字出现的次数,比如D出现了1次,那么第二个序列就是D1,对于第二个序列D1,第三个序列这样描述:D出现1次,1出现1次,所以是D111……以此类推,输出第n个序列。
【解题思路】
这题用string的话会有一个测试点超时,可以用字符数组。num记录重复数字,每当遇到前后两个字符不同时,则将其记录到b数组中,这里需要注意处理a数组的最后一个字符,因为要判断当前字符和下一个字符的关系,所以for循环只能到strlen(a)-1为止,所以最后一个字符需要放在循环外面单独处理。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
char a[maxn],b[maxn];
int main()
{
int step;
scanf("%s %d",a,&step);
step--;
while(step--)
{
int num=1,cnt=0;
for(int i=0;i<strlen(a)-1;i++)
{
if(a[i]!=a[i+1])
{
b[cnt++]=a[i];
b[cnt++]=num+'0';
num=1;
}
else num++;
}
b[cnt++]=a[strlen(a)-1];
b[cnt++]=num+'0';
strcpy(a,b);
}
printf("%s\n",a);
return 0;
}
1141 PAT Ranking of Institutions
(25)排序+map
【题意】
给出每个学生的id、分数、学校,学校名称不区分大小写,输出学校排名、学校名称、总加权成绩、学校参赛人数。学校名称输出时候以小写方式输出。
【解题思路】
还是常规套路排序,用map记录没有重复的学校的编号,但本题有个坑,最后的分数应该先用double存储最后转换成int,如果在运算过程中就用int的话最后有个测试点会过不了。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
double sc[maxn];
struct Node
{
string name;
int num=0,score=0;
}node[maxn];
bool cmp(Node a,Node b)
{
if(a.score!=b.score)return a.score>b.score;
else
return(a.num==b.num)?a.name<b.name:a.num<b.num;
}
int main()
{
int n,cnt=0;
map<string,int>m;
scanf("%d",&n);
while(n--)
{
string s,name;
int score;
cin>>s>>score>>name;
transform(name.begin(),name.end(),name.begin(),::tolower);
if(!m.count(name))m[name]=cnt++;
node[m[name]].name=name;
if(s[0]=='B')sc[m[name]]+=score/1.5;
else if(s[0]=='A')sc[m[name]]+=score;
else if(s[0]=='T')sc[m[name]]+=score*1.5;
node[m[name]].num++;
}
for(int i=0;i<cnt;i++)
node[i].score=sc[i];
printf("%d\n",cnt);
sort(node,node+cnt,cmp);
int rak=1,num=1;
if(cnt>0)
{
cout<<rak<<" "<<node[0].name<<" "<<node[0].score<<" "<<node[0].num<<endl;
for(int i=1;i<cnt;i++)
{
if(node[i].score==node[i-1].score)
{
num++;
cout<<rak<<" "<<node[i].name<<" "<<node[i].score<<" "<<node[i].num<<endl;
}
else
{
rak+=num;
cout<<rak<<" "<<node[i].name<<" "<<node[i].score<<" "<<node[i].num<<endl;
num=1;
}
}
}
return 0;
}
1142 Maximal Clique
(25)模拟
【题意】
clique是一个点集,在一个无向图中,这个点集中任意两个不同的点之间都是相连的。maximal clique是一个clique,这个clique不可以再加入任何一个新的结点构成新的clique。点编号从1~nv,给出ne条边,以一对结点编号的方式给出。然后给出m条询问,每个询问是一个点集合,问这个点集合是否是maximal clique、是否是clique。
【解题思路】
先判断是不是clique,因为数据很小直接暴力判断即可,存在一条边没有想连则不是。然后再遍历所有不是查询的顶点,看是否存在一个顶点与查询顶点都存在边,如果有则输出Not maximal,反之则输出Yes。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
int edge[maxn][maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
edge[u][v]=1;
edge[v][u]=1;
}
int k;
scanf("%d",&k);
while(k--)
{
set<int>s;
int nv,flag=1,flag2=0,a[maxn];
scanf("%d",&nv);
for(int i=0;i<nv;i++)
{
int x;
scanf("%d",&x);
s.insert(x);
a[i]=x;
}
for(int i=0;i<nv;i++)
{
for(int j=i+1;j<nv;j++)
{
if(!edge[a[i]][a[j]])
{
flag=0;
break;
}
}
if(!flag)break;
}
if(!flag)printf("Not a Clique\n");
else
{
for(int i=1;i<=n;i++)
{
if(!s.count(i))
{
for(int j=0;j<nv;j++)
{
if(!edge[i][a[j]])break;
if(j==nv-1)flag2=1;
}
}
if(flag2)break;
}
if(flag2)printf("Not Maximal\n");
else printf("Yes\n");
}
}
return 0;
}
1143 Lowest Common Ancestor
(30)BST树+模拟
【题意】
给出一棵二叉搜索树的前序遍历,问结点u和v的共同最低祖先是谁。
【解题思路】
刚开始以为要建树但事实上不用,简单模拟之后可以发现只要存在一个节点在u和v之间,最先找到的那个节点肯定是题意所需节点,因为根据BST树的定义来看,根节点肯定大于左子树,小于右子树,又因为题中给出的是先序序列,所以根节点肯定在左子树和右子树节点的前面。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int pre[maxn];
int main()
{
int n,m;
set<int>s;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d",&pre[i]);
s.insert(pre[i]);
}
while(n--)
{
int u,v,a;
scanf("%d%d",&u,&v);
for(int i=0;i<m;i++)
{
a=pre[i];
if((a<=u && a>=v) ||a<=v && a>=u)
break;
}
if(!s.count(u) && !s.count(v))
printf("ERROR: %d and %d are not found.\n",u,v);
else if(!s.count(u) || !s.count(v))
printf("ERROR: %d is not found.\n",s.count(u)?v:u);
else if(u==a || a==v)
printf("%d is an ancestor of %d.\n",u==a?u:v,u==a?v:u);
else
printf("LCA of %d and %d is %d.\n",u,v,a);
}
return 0;
}
1144 The Missing Number
(20)set的使用
【题意】
给n个数,找一个最小的没有出现在这个序列中的正整数。
【解题思路】
用set记录出现过的正整数,并且更新这个序列的最大值,作为接下来要查找的上界,然后就直接从1开始暴力查找就可以了。
【代码】
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,max1=0;
scanf("%d",&n);
set<int>s;
while(n--)
{
int x;
scanf("%d",&x);
if(x>max1)max1=x;
if(x>0)s.insert(x);
}
for(int i=1;i<=max1+1;i++)
{
if(!s.count(i))
{
printf("%d\n",i);
break;
}
}
return 0;
}
1145 Hashing – Average Search Time
(25)二次方探查法
【题意】
给定一个序列,用平方探测法解决哈希冲突,然后给出m个数字,如果这个数字不能够被插入就输出”X cannot be inserted.”,然后输出这m个数字的平均查找时间。
【解题思路】
首先二次方探查法之前也做过,就是这个index = (key + step * step) % size,0<=step<size判断index是否存在,然后查找时同样按照这个思路,但是遍历的时候size处应该用等于,因为如果一个数没有找到的话,所用的时间时size+1,最后再取平均即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int vis[maxn];
int judge(int x)
{
if(x==1)return 0;
else if(x==1)return 1;
else
{
for(int i=2;i<=sqrt(x);i++)
{
if(x%i==0)return 0;
}
}
return 1;
}
int main()
{
int msize,n,m;
scanf("%d%d%d",&msize,&n,&m);
while(!judge(msize))msize++;
while(n--)
{
int x,flag=1;
scanf("%d",&x);
for(int i=0;i<msize;i++)
{
int t=(x+i*i)%msize;
if(!vis[t])
{
vis[t]=x;
flag=0;
break;
}
}
if(flag)printf("%d cannot be inserted.\n",x);
}
int sum=0;
for(int i=0;i<m;i++)
{
int x,j=0;
scanf("%d",&x);
for(int k=0;k<=msize;k++)
{
j++;
int t=(x+k*k)%msize;
if(vis[t]==x || vis[t]==0)break;
}
sum+=j;
}
double ans=(sum*1.0)/m;
printf("%.1f\n",ans);
return 0;
}
1146 Topological Order
(25)拓扑排序
【题意】
给n个顶点与m条边,再给出k次查询,看每次查询的序列是否构成拓扑排序。
【解题思路】
逐个检查每个序列即可,当这个顶点的入度为0,则将以这个顶点为起点的其他顶点的入度-1,并继续排序。若遍历过程中出现某个顶点的入度不为0,那么退出循环,说明这个序列不构成拓扑排序。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int edge[maxn][maxn],in[maxn],a[maxn],ans[maxn],tempin[maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
edge[u][v]=1;
in[v]++;
}
int q,cnt=0;
scanf("%d",&q);
for(int k=0;k<q;k++)
{
for(int i=1;i<=n;i++)
tempin[i]=in[i];
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int j=0,flag=0;
while(!flag && j<n)
{
if(tempin[a[j]]==0)
{
for(int i=1;i<=n;i++)
{
if(edge[a[j]][i] && tempin[i]>0)
tempin[i]--;
}
j++;
}
else
{
flag=1;
break;
}
}
if(flag)ans[cnt++]=k;
}
for(int i=0;i<cnt;i++)
i==0?printf("%d",ans[i]):printf(" %d",ans[i]);
printf("\n");
return 0;
}
1147 Heaps
(30)堆排序
【题意】
判断一个序列是大顶堆还是小顶堆,或者都不是,并输出后序序列。
【解题思路】
其实和堆排序也没什么特别大的关系……如果父节点比孩子节点都大的话是大顶堆,反之则是小顶堆,然后用dfs遍历输出后序序列即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n,m,a[maxn],f;
void dfs(int x)
{
if(2*x+1<n)dfs(x*2+1);
if(2*x+2<n)dfs(x*2+2);
f==0?printf("%d",a[x]):printf(" %d",a[x]);
f=1;
}
int main()
{
scanf("%d%d",&m,&n);
while(m--)
{
int flag=1;
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
{
if(2*i+1<n && a[i]<a[2*i+1])
{
flag=0;
break;
}
if(2*i+2<n && a[i]<a[2*i+2])
{
flag=0;
break;
}
}
if(flag)printf("Max Heap\n");
else
{
int flag2=1;
for(int i=0;i<n;i++)
{
if(2*i+1<n && a[i]>a[2*i+1])
{
flag2=0;
break;
}
if(2*i+2<n && a[i]>a[2*i+1])
{
flag2=0;
break;
}
}
if(flag2)printf("Min Heap\n");
else printf("Not Heap\n");
}
f=0;
dfs(0);
printf("\n");
}
return 0;
}
1148 Werewolf – Simple Version
(20)暴力模拟
【题意】
狼人杀。一局游戏里有两只狼,并且只有一只说谎,然后其余都是村民,村民中有一人说谎。找出编号最小的两只狼。
【解题思路】
因为数据很小直接暴力模拟即可。哎考试的时候还是不够冷静……当时是在找暴力找说谎的人,其实只要找狼就可以了。
建立a集合为狼,b集合为村民,c集合为说谎者。然后只要理清思路就可以了。
如果k的陈述是p[k],当p[k]为狼时,如果它不在狼集合里,说明k说谎了,将他放入c,并且将p[k]放入b;如果p[k]不是狼,但是在狼的集合里,说明k说谎了,将他放入c,如果不在狼集合里,说明k没有说谎,将p[k]放入b。
然后根据条件判断集合c是否只有2个说谎者,并且一个是狼一个是村民。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
int p[maxn];
int main()
{
int n,i,j,flag2=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&p[i]);
for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
set<int>a,b,c;//a是狼,b是村民,c是说谎
a.insert(i);
a.insert(j);
int flag=1;
for(int k=1;k<=n;k++)
{
if(p[k]<0)
{
if(!a.count(abs(p[k])))
{
c.insert(k);
b.insert(abs(p[k]));
}
else
{
if(!a.count(k))b.insert(k);
}
}
else
{
if(a.count(abs(p[k])))c.insert(k);
else b.insert(abs(p[k]));
}
}
if(c.size()!=2)flag=0;
else
{
int cnt1=0,cnt2=0;
for(set<int>::iterator it=c.begin();it!=c.end();it++)
{
if(a.count(*it))cnt1++;
if(b.count(*it))cnt2++;
}
if(cnt1!=1 || cnt2!=1)flag=0;
}
if(flag)
{
flag2=1;
break;
}
}
if(flag2)break;
}
if(flag2)printf("%d %d\n",i,j);
else printf("No Solution\n");
return 0;
}
1151 LCA in a Binary Tree
(30)LCA+中序、先序转换
【题意】
分别给一个中序和先序序列,再给出m次询问,每次询问两个节点,寻找这两个节点的最近公共祖先,并按照规定格式输出。
【解题思路】
这里只要搞清楚,当询问的两个节点在当前根节点的左边,那么他们的最近公共祖先一定在左子树,如果两个节点在当前根节点的右边,那么需要遍历右子树,如果一个在左一个在右,那么该根节点就是他们的最近公共祖先。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int n,m;
map<int,int>rin;
int pre[maxn],in[maxn];
void lca(int root,int start,int end,int a,int b)
{
if(start>end)return;
int rootidx=rin[pre[root]],lidx=rin[a],ridx=rin[b];
if(rootidx>lidx && rootidx>ridx)
lca(root+1,start,rootidx-1,a,b);
else if(rootidx<lidx && rootidx<ridx)
lca(root+rootidx-start+1,rootidx+1,end,a,b);
else if(rootidx==lidx)
printf("%d is an ancestor of %d.\n",a,b);
else if(rootidx==ridx)
printf("%d is an ancestor of %d.\n",b,a);
else if((rootidx<lidx && rootidx>ridx) ||(rootidx>lidx && rootidx<ridx))
printf("LCA of %d and %d is %d.\n",a,b,pre[root]);
}
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&in[i]);
rin[in[i]]=i;
}
for(int i=1;i<=n;i++)
scanf("%d",&pre[i]);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
if(!rin.count(u) && !rin.count(v))
printf("ERROR: %d and %d are not found.\n",u,v);
else if(!rin.count(u) && rin.count(v))
printf("ERROR: %d is not found.\n",u);
else if(rin.count(u) && !rin.count(v))
printf("ERROR: %d is not found.\n",v);
else lca(1,1,n,u,v);
}
}
1155 Heap Paths
(30)完全二叉堆
【题意】
给出一个完全二叉堆的序列,输出每个叶节点的路径,并且判断该堆为最大堆还是最小堆。
【解题思路】
最大堆、最小堆的判断还是简单的,然后遍历路径的话因为它是完全二叉堆,所以不需要建树,直接利用二叉树左右子树的性质进行深搜即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int h[maxn],n,flag=0;
vector<int>v;
int judgemin()
{
for(int i=2;i<=n;i++)
if(h[i/2]>h[i])return 0;
return -1;
}
int judgemax()
{
for(int i=2;i<=n;i++)
if(h[i/2]<h[i])return 0;
return 1;
}
void dfs(int x)
{
if(x>n)return;
if(2*x>n && 2*x+1>n)
{
v.push_back(h[x]);
for(int i=0;i<v.size();i++)
(i==0)?printf("%d",v[i]):printf(" %d",v[i]);
printf("\n");
v.pop_back();
return;
}
v.push_back(h[x]);
dfs(2*x+1);
dfs(2*x);
v.pop_back();
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
int flag=judgemin();
if(flag!=-1)flag=judgemax();
dfs(1);
if(flag==1)printf("Max Heap\n");
else if(flag==-1)printf("Min Heap\n");
else printf("Not Heap\n");
}