东北农业大学2020寒假acm培训day3数据结构基础
第一次写,也别指望我写好多,并且能力有限,很多题想复杂了,求大佬轻骂
题目传送门 点我点我
PROBLEM A 消消乐
为了简化问题,现在只在一行上进行消消乐,而且只要相邻元素相同就可以消掉。
现在给出一个行消消乐的序列,问可以消除多少次?
思路:每2个相邻一样的便需要消除,可以从第一个开始一个一个判断邻位是否相等,但假如拿数组存的话,每次删除后需要把后面所有数字往前移动2位,时间复杂度太大,所以建议直接使用栈做,从第一位开始放入栈,假如栈空,直接放入,栈非空时,判断栈顶元素与代插入元素是否相等,相等时栈顶元素直接退栈,不相等时进栈
重点
假如你的栈和我一样是全局变量,每次跑完一组数据的时候,一定一定一定要清空栈
代码:
#include <bits/stdc++.h>
#include <stack>
#include <string>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T,i;
string s1;
stack <int> qwe;
cin>>T;
while(T--) {
int num=0;
cin>>s1;
int n=s1.size();
for(i=0; i<n; i++) {
if(qwe.empty() || qwe.top()!=s1[i])
qwe.push(s1[i]);
else {
num++;
qwe.pop();
}
}
cout<<num<<"\n";
while(!qwe.empty())
qwe.pop();
}
return 0;
}
PROBLEM B 队列操作
数据结构基础之一——队列
队列有五种基本操作,插入队尾、取出队首、删除队首、队列大小、清空队列
思路: 没啥好说的,就是个简单模拟,甚至都不用你自己写各种函数原型,直接用queue的头文件就可以了,细心点就行,同样的,全局变量的队列,记得清空(有一说一,我似乎写麻烦了)
代码:
#include <bits/stdc++.h>
#include <queue>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T,n,q;
cin>>T;
string s1;
queue <int> a;
while(T--) {
cin>>n;
while(n--) {
cin>>s1;
switch(s1[0]) {
case'P': {
if(s1[1]=='O') {
if(a.empty())
cout<<-1<<"\n";
else
a.pop();
} else {
cin>>q;
a.push(q);
}
break;
}
case'T': {
if(a.empty())
cout<<-1<<"\n";
else
cout<<a.front()<<"\n";
break;
}
case'S': {
cout<<a.size()<<"\n";
break;
}
case'C': {
while(!a.empty())
a.pop();
break;
}
}
}
while(!a.empty())
a.pop();
}
return 0;
}
PROBLEM C 树的深度
给你一个含有n个结点的树,然后有Q次询问,每一次询问,问你这个结点的深度是多少
思路: 问某个结点的深度,其实就是他的父亲结点可以推到哪,
假如他前面没有父亲节点,那他就是根节点,深度为1,他有父亲,但他父亲没有父亲了,那他度就是2,以此类推,所以可以直接通过循环,得出他一直到根节点,经历了几任爸爸
代码:(我代码写复杂了,直接copy罗学姐的了,看不懂的去问他)
#include<bits/stdc++.h>
using namespace std;
int fa[100005];
int main() {
int n;
int t;
cin>>t;
while(t--) {
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&fa[i]);
int q;
scanf("%d",&q);
while(q--) {
int x;
scanf("%d",&x);
int ans=0;
while(x) {
x=fa[x];
ans++;
}
cout<<ans<<endl;
}
}
return 0;
}
PROBLEM D 最近距离
给出一个有向图,请找出从s到e的最小距离并且必须仅经过中间某点k
思路: 全场最最最水的一题了,但是就2个人过,因为题意太模糊了,把题意说一下就行,s到e,经过且仅经过一点,所以拿样例举例,
1到1,除非是1->2,2->3,3->4,4->5,5->1,经过了不止一个点,所以输出-1,
1到2,要么不经过其他任何点,要么得像之前那样跑一个循环,也不行,
1到3,1->2,2->3,刚好只经过2,成立,
1到3,1->2,2->3,3->4,经过了2和3一共两个点,不成立
所以本题其实跑一个循环就够了,看代码
代码:
#include <bits/stdc++.h>
using namespace std;
int num[1500][1500];
int main()
{
int T,x,y,z,i,j,n,m,q;
//cin>>T;
//while(T--)
{
memset(num,0,sizeof(num));
cin>>n>>m>>q;
//for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
cin>>x>>y>>z;
num[x][y]=z;
}
while(q--)
{
int minn=10000000;
cin>>x>>y;
for(i=1;i<=n;i++)
{
if(num[x][i] && num[i][y] && (i!=x)&& (i!=y))
{
minn=min(minn,num[i][y]+num[x][i]);
}
}
if(minn!=10000000)
cout<<minn<<"\n";
else
cout<<-1<<"\n";
}
}
return 0;
}
PROBLEM E 子结点
给你一个含有n个结点的树(结点编号为1~n),然后有Q次询问,每一次询问,问你这个结点的所有子结点编号。
思路: 有一说一,C题跟这题异曲同工之妙,C题找父亲,这题一样找父亲就完事了,并且只要找一次,找到父亲结点后,创立一个vector容器,在对应的父亲结点里,放入自己的编号就行(我一开始看错题了,以为是子孙结点,把祖先找光了,代码有点麻烦了,但改一下就能当子孙结点了,感兴趣的话可以自己试下)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
P a[100005];
int num[100005];
vector<int>vbn[100005];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T,n,i,q,x;
cin>>T;
while(T--) {
memset(num,0,sizeof(num));
memset(a,0,sizeof(a));
cin>>n>>q;
for(i=1; i<=n; i++) {
vbn[i].clear();
}
for(i=1; i<=n; i++) {
cin>>x;
a[i].first=x;
a[i].second=i;
x=i;
if(a[x].first) {
num[a[x].first]++;
x=a[x].first;
vbn[x].push_back(i);
}//把这里的if改成where,就可以求出所有的子孙结点了
}
while(q--) {
cin>>x;
cout<<num[x];
for(i=0; i<num[x]; i++) {
cout<<" "<<vbn[x][i];
}
cout<<"\n";
}
}
return 0;
}
PROBLEM F 第k层祖先
给你一个满二叉树,有Q次询问,每一次询问让你找一个编号为x的结点在深度为k的祖先节点的编号是多少?
思路: 无fuck可说,不懂满二叉树的话可以点这里点我,懂吗?你把定义看看,这题就会了,我直接贴代码了
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T;
long long x,k,q;
cin>>T;
while(T--)
{
cin>>x>>k;
q=pow(2,k);//强烈建议换成下面的,我自己偷懒这么写的
//q=1LL<<x
if(x<q)
cout<<-1<<"\n";
else
{
while(x>=q)
x/=2;
cout<<x<<"\n";
}
}
return 0;
}
PROBLEM G 子节点问题2
给你n个节点,告诉你每一个节点下面的子节点的数量,问你是否可以构成一颗树
思路: (无fuck说)^2,百度定义去,n个结点的树的边有且仅有n-1条边,你算下总值是不是n-1就行
重点: 空树也是树,空树也是树,空树也是树,空树也是树,
空树也是树,空树也是树,空树也是树
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
long long int n,i,a,sum;
cin>>n;
sum=0;
for(i=1;i<=n;i++)
{
cin>>a;
sum+=a;
}
if(n==0)
cout<<"YES\n";
else
if(sum==n-1)
{
cout<<"YES\n";
}
else
cout<<"NO\n";
return 0;
}
PROBLEM H ++的院子
++的院子里有一种很奇怪的结构,上面有4个结点,n条边。他想知道这种结构是不是树。
**思路:**开门见山,我的思路,绝对不是最优解,我的思路首先判断,边是不是3个,因为一共4个点,必须是4-1条边才能构成树,
或者假如出现了自己连自己,也可以直接排除
确定3条边后,我们可以很轻易的举出很多种情况,那么我们反向思维,什么时候不成立
所以我通过邻接矩阵存的边的关系,判断是否存在某2个点重复连了2次边,或者某个点从始至终没跟其他点连接过,如果成立,就不是树
具体见代码实现
代码:
#include <bits/stdc++.h>
using namespace std;
int qwe[5][5];
int sum[5];
int main()
{
int T,n,flag=1,i,j,x,y;
cin>>T;
while(T--)
{
memset(qwe,0,sizeof(qwe));
memset(sum,0,sizeof(sum));
flag=1;
cin>>n;
for(i=0;i<n;i++)
{
cin>>x>>y;
if(x==y)//自环直接死
flag=0;
qwe[x][y]++;
qwe[y][x]++;
}
if(n==3)
{
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++)
{
if(qwe[i][j]>=2)//同一边出现2次
{
flag=0;
break;
}
}
if(!flag)
break;
}
for(j=1;j<=4;j++)
{
int asd=0;
for(i=1;i<=4;i++)
{
if(qwe[i][j])
asd++;
}
sum[j]=asd;
if(sum[j]==0)//假如某个点没有跟任何点相连,也不是树
{
flag=0;
break;
}
}
}
else
flag=0;
if(flag)
cout<<"++\n";
else
cout<<"--\n";
}
return 0;
}
PROBLEM I Stick
现在有n个木棒,每一个木棒都有一个长度,现在想让你将这n个木棒合并成为一个长的木棒,已知合并的时候每一次只能选择任意两根木棒进行合并,合并的代价为这两个木棒的长度,现在让你求所有可能方案中合并的代价总和最小为多少?
思路: oj上的原题,你要是不会就代表你没好好补题了,(然后数据上看今天之前似乎就我写过。。。。并且那套题我刚好只写了这一题,很巧合)传送门来了点我,亲
并且 poj上也有一道同样的题,你同样可以试试,给你个传送门我就是,点我就行了
优先队列模板题,你要是用霍夫曼树做的话poj上是不会超时的,(咱们学校oj我没试,逃)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T,i;
cin>>T;
while(T--)
{
ll n=0,x=0,ans=0;
priority_queue<ll,vector<ll>,greater<ll> >q;
cin>>n;
for(i=1;i<=n;i++)
{
cin>>x;
q.push(x);
}
while(q.size()>=2)
{
ll a=q.top();q.pop();
ll b=q.top();q.pop();
ans+=a+b;
q.push(a+b);
}
cout<<ans<<"\n";
}
return 0;
}
PROBLEM J 字符价值
我代码突然tle了,以及我累了,逃,等我改下再来