《第三次上机实验》解题报告
第1题 重复计数
先讲一下我在考试时的想法吧:二叉查找树(bst)但是我太菜了只会写最基本的bst同时为了图方便我使用了顺序存储,于是导致只要左边多了一层,总结点数就要*2,于是只要偏了四五层就必定会爆空间。而同学们居然暴力就能过,太痛了!!!!!!!
思想;谁是倒一,我是倒一!!!!!!!!于是回去之后学了一点map,用map直接逃课,将数字同时输入map,和一个记录顺序的数组,之后按数组记录的顺序输出。
时间复杂度;O(n)
总结;太离谱了,暴力直接过,要不就是map,谷老师说的排序怎么排我还是搞不懂。
#include<stdio.h>
#include<map>
int y[51000];
using namespace std;
map<int, int> maps;
int main()
{
int n, a, b, c, d;
scanf("%d", &n);
for (a = 1; a <= n; a++)
{
scanf("%d", &b);
y[a] = b;
maps[b]++;
}
for (a = 1; a <= n; a++)
{
if (maps[y[a]] != 0)
{
printf("%d %d\n",y[a], maps[y[a]]);
maps[y[a]] = 0;
}
}
}
第2题 二叉树加权距离
考试的时候,用广搜完成了部分,主要没想到询问的节点可以在根的同一子树当中,于是就没写找最小祖父节点的代码,等写完后就不好改了。
思想;回去了之后用了一下递归的深搜,找到节点,保存路径,保存深度,然后通过保存的路径找到最小祖父节点,我想的没错,但是因为递归爆空间了。于是我将保存的边倒过来,从节点找到根,保存深度,然后将深度统一,一起往上走,知道碰面。将路径长度输出。于是这样什么dfs,bfs都不用,直接四个循环就出来了。
时间复杂度;O(n);
总结;将边倒过来存,只保存从各节点的父亲节点,直到根,然后一个一个的找过去就行了。
#include<stdio.h>
int x[100100];
int main() {
int n, a, b, c,i,j,t,t1=0,t2=0,s=0;
scanf("%d", &n);
for (a = 1; a < n; a++){
scanf("%d %d", &b, &c);
x[c]=b;
}
scanf("%d %d", &i, &j);
c = i;
while (c != 1){
t1++;
c = x[c];
}
c= j;
while (c != 1){
t2++;
c = x[c];
}
while(t1 < t2){
j = x[j];
s += 2;
t2--;
}
while (t1 > t2){
i = x[i];
s += 3;
t1--;
}
while (i != j){
j = x[j];
i = x[i];
s += 5;
}
printf("%d", s);
return 0;
}
第3题 发红包
关键路径问题+森林,在考试的时候想到的是bfs但是写不出来
思想;用两个vector分别存两个方向的边,用一个数组存入读,用一个数组存最小深度,用一个数组当栈来进行bfs。开始先遍历将入度为0的点存入,然后进行bfs改变最小深度,最后遍历,将深度累加,若还有入度大与一的输出-1,不然输出累和。
时间复杂度;O(m)
总结;本来是最简单的一题,但是放在第三个,我被前两个卡住了,最后没时间写了,开始没想到关键路径,又走了弯路,最后也没编完。
#include<stdio.h>
#include<vector>
using namespace std;
vector<int>y[10100];
vector<int>z[10100];
int x[10100] = {};
int k[101000] = {};
int t[10100] = {};
int main()
{
int n, m, a, b, c,s=0,r=1,l=1;
scanf("%d %d", &n, &m);
for (a = 1; a <= m; a++)
{
scanf("%d %d", &b, &c);
z[b].push_back(c);
y[c].push_back(b);
x[c]++;
}
for (a = 1; a <= n; a++)
{
if (x[a] == 0)
{
k[r] = a;
r++;
}
}
for (l = 1; l <= r; l++)
{
a = k[l];
for (b = 0; b < z[a].size(); b++)
{
x[z[a][b]]--;
if (x[z[a][b]] == 0)
{
k[r] = z[a][b];
r++;
}
}
c = 0;
for (b = 0; b < y[a].size(); b++)
{
if (t[y[a][b]] > c)
c = t[y[a][b]];
}
t[a] = c + 1;
}
b = 0;
for (a = 1; a <= n; a++)
{
if (x[a] != 0)
{
b = 1;
break;
}
s += t[a];
}
if (b == 1)
{
printf("-1");
}
else
{
printf("%d", n * 887 + s);
}
return 0;
}
第4题 数据结构设计I
在考试的时候想骗一点分,没想到是一点都骗不到呀(悲)。
思想;想了半天想不到一种不用stl的方法,于是用了一个queue和multiset来实现,一旦插入,就同时向两个链表同时插入,一旦输出,就从双端队列尾部输出然后从对应的multiset中输出,每次取反就将指针r=0/1来取反,一旦输出,若r=0输出multiset的头,若r=1输出multiset的尾。
时间复杂度;O(n*log n)
总结;想不到一种不用stl的方法,要注意当r=1时输入应*-1,同时弹出时应判空,不然会段错误。
#include<stdio.h>
#include<set>
#include<queue>
using namespace std;
multiset<int,greater<int>>x;
deque<int>y;
int main()
{
int m, r=0, a, b, c, d;
char ch;
scanf("%d", &m);
for (a = 1; a <= m; a++)
{
scanf("%c", &ch);
while(ch!='I'&& ch != 'D'&& ch != 'R'&& ch != 'M')
scanf("%c", &ch);
if (ch == 'I')
{
scanf("%d",&b);
if (r == 1)
b = b * -1;
x.insert(b);
y.push_front(b);
}
if (ch == 'D'&&x.size() != 0)
{
b = y.back();
y.pop_back();
x.erase(x.find(b));
}
if (ch == 'R')
{
if (r == 0)
r = 1;
else
r = 0;
}
if (ch == 'M'&&x.size()!=0)
{
if (r == 0)
{
multiset<int>::iterator it = x.begin();
printf("%d\n", *it);
}
else
{
multiset<int>::reverse_iterator its = x.rbegin();
printf("%d\n", *its*-1);
}
}
}
return 0;
}
第5题 奖学金
孔姐姐的左偏树,看了孔姐姐的ppt又上了csdn上看了2个小时,眼睛说看懂了,脑子却全忘了,最后完全抄的孔姐姐ppt的代码。
思想;可合并堆,可以在log时间内完成两个堆的合并。merge(x,y):将堆顶x和y的两个堆合并,返回合并后的堆顶。删除操作,直接合并左右儿子。
时间复杂度;O(n*log n)
总结;看了半天看不懂,只能抄代码了,结果不愿意用swap函数,手写了个交换函数,结果只能交换形参,找了半天,我寻思着我不是照抄的么,怎么还能错,改了半天才找到时swap的问题。
#include<stdio.h>
#include <iostream>
using namespace std;
int n, m;
struct cell {
int ls, rs, dist, val;
}tr[100100];
int fa[100100];
bool del[100100];
int find(int a)
{
if (fa[a]==a)
return a;
return fa[a] = find(fa[a]);
}
int merge(int x, int y) { //合并
int t;
if (!x || !y) return x | y; //???不理解?????
if (tr[x].val < tr[y].val || (tr[x].val == tr[y].val && x > y))
swap(x,y);
tr[x].rs = merge(tr[x].rs, y);
if (tr[tr[x].ls].dist < tr[tr[x].rs].dist)
swap(tr[x].ls,tr[x].rs);
tr[x].dist = tr[tr[x].rs].dist + 1;
return x;
}
void erase(int x)
{
del[x] = 1;
fa[x] = fa[tr[x].ls] = fa[tr[x].rs] = merge(tr[x].ls, tr[x].rs);
}
int main()
{
int a, b, c,d,e,f;
scanf("%d %d", &n, &m);
for (a = 1; a <= n; a++)
{
scanf("%d", &b);
fa[a] = a;
tr[a].val = b;
}
for (a = 1; a <= m; a++)
{
scanf("%d", &b);
if (b == 1)
{
scanf("%d %d", &c, &d);
if (find(c)==find(d)||del[c]||del[d])
continue;
c = find(c);
d = find(d);
fa[c] =fa[d]= merge(c,d);
}
if (b == 2)
{
scanf("%d", &c);
if (del[c])
continue;
erase(find(c));
}
if (b == 3)
{
scanf("%d", &c);
if (del[c]) {
printf("-1\n");
continue;
}
printf("%d\n", tr[find(c)].val);
}
}
return 0;
}