第六次上机实验解题报告

前言

欢迎讨论,欢迎纠错,本次报告,证明从简。

7 - 1 高精度加法

解题思路

简而言之,使用数组模拟列竖式的过程,例如我们可以用 A [ i ] A[i] A[i] 表示某个数的 1 0 i 10^i 10i 位上的值。

代码实现

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 210 + 5;

char buff[maxn];
struct bigInt {
	int mem[maxn];
	int len;
	int flag;
	void input() {
		scanf("%s", buff);
		len = strlen(buff);
		memset(mem, 0x00, sizeof(mem));
		if(buff[0] == '-') {
			flag = 1; /// < 0
			for(int i = 0; i < len-1; i ++) {
				mem[i] = buff[len - i - 1] - '0';
			}
			len --;
		}else {
			flag = 0; /// > = 0
			for(int i = 0; i < len; i ++) {
				mem[i] = buff[len - i - 1] - '0';
			}
			if(len == 1 && mem[0] == 0) {
				len --;
			}
		}
	}
	void output() {
		//if(flag) putchar('-');
		if(len == 0) {
			printf("0");
		}else {
			for(int i = len - 1 ; i >= 0; i --) {
				printf("%d", mem[i]);
			}
		}
	}
};

bigInt& operator +=(bigInt& A, bigInt& B) {
	A.len = max(A.len, B.len);
	for(int i = 0; i < A.len; i ++) {
		A.mem[i] += B.mem[i];
		A.mem[i+1] += A.mem[i] / 10;
		A.mem[i] %= 10;
	}
	if(A.mem[A.len] != 0) {
		A.len ++;
	}
	return A;
}

bool operator>=(bigInt& A, bigInt& B) {
	if(A.len != B.len) return A.len > B.len;
	for(int i = A.len-1; i >= 0; i --) {
		if(A.mem[i] != B.mem[i]) {
			return A.mem[i] > B.mem[i]; 
		} 
	}
	return true; /// same
}

bigInt operator -=(bigInt& A, bigInt& B) {
	for(int i = 0; i < B.len; i ++) {
		A.mem[i] -= B.mem[i];
		while(A.mem[i] < 0) {
			A.mem[i] += 10;
			A.mem[i + 1] --;
		}
	}
	while(A.len > 0 && A.mem[A.len - 1] == 0) {
		A.len --;
	}
	return A;
}

bigInt Ans, Bns, Tmp;
int main() {
	int n; scanf("%d", &n);
	for(int i = 0; i < n; i ++) {
		Tmp.input();
		//Tmp.output(); putchar('\n');
		if(Tmp.flag) {
			/// Tmp < 0
			Bns += Tmp;
		}else {
			Ans += Tmp; 
		}
	}
	//Ans.output(); putchar('\n');
	//Bns.output(); putchar('\n');
	if(Ans >= Bns) { /// ans not net
		Ans -= Bns;
		Ans.output();
	}else {
		putchar('-');
		Bns -= Ans;
		Bns.output(); 
	}
	return 0;
}

7 - 2 二叉树加权距离

解题思路

先通过一次 dfs 找到每个节点在二叉树中的深度,为了编程简单我们记根节点的深度为 1 1 1

代码实现

#include <cstdio>
//#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 100000 + 5;

struct vector {
	int mem[3], cnt;
	void push_back(int x) {
		mem[cnt ++] = x;
	}
	int size() {
		return cnt;
	}
	int& operator[](int index) {
		return mem[index];
	}
};

vector nxt[maxn];
void addedge(int f, int t) {
	nxt[f].push_back(t);
	nxt[t].push_back(f);
}

int fa[maxn], dep[maxn];
void dfs(int x, int f) {
	fa[x] = f;
	dep[x] = dep[f] + 1;
	for(int i = 0; i < nxt[x].size(); i ++) {
		int t = nxt[x][i];
		if(t == f) continue;
		dfs(t, x);
	}
}

int lca(int x, int y) {
	if(dep[x] < dep[y]) {
		swap(x, y); /// dep[x] >= dep[y]
	}
	while(dep[x] > dep[y]) {
		x = fa[x];
	}
	while(x != y) {
		x = fa[x];
		y = fa[y];
	}
	return x;
}

int main() {
	int n; scanf("%d", &n);
	for(int i = 1; i < n; i ++) {
		int a, b; scanf("%d%d", &a, &b);
		addedge(a, b);
	}
	dfs(1, 0);
	int u, v; scanf("%d%d", &u, &v);
	int p = lca(u, v);
	printf("%d", (dep[u] - dep[p])*3 + (dep[v] - dep[p])*2);
	return 0;
}

7 - 3 修轻轨

解题思路

利用 Kruskal 的基本思想,对所有边进行排序。按照边权从小到大枚举每一条边 ( u , v ) (u,v) (u,v),并将这条边 u u u v v v 当前所在的连通块在并查集上进行合并。当 1 1 1 号节点与 n n n 号节点第一次位于同一个连通块时,此时最后一条被考虑的边就是答案。

代码实现

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 100000 + 6;

int fa[maxn], bcnt;

void init(int n) {
	for(int i = 1; i <= n; i ++) {
		fa[i] = i;
	}
	bcnt = n;
}

int find(int x) {
	if(x == fa[x]) return x;
	else return fa[x] = find(fa[x]);
}

struct Edge {
	int f, t, c;
} es[2*maxn];

bool cmp(Edge& A, Edge& B) {
	return A.c < B.c;
}

void link(int x, int y) {
	x = find(x);
	y = find(y);
	fa[x] = y;
}

int main() {
	int n, m; scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i ++) {
		int a, b, c; scanf("%d%d%d", &a, &b, &c);
		es[i] = (Edge) {a, b, c};
	}
	sort(es + 1, es + m + 1, cmp);
	init(n);
	int ans = 0;
	for(int i = 1; i <= m; i ++) {
		int f = es[i].f, t = es[i].t, c = es[i].c;
		if(find(f) != find(t)) {
			bcnt --;
			link(f, t);
			ans = c;
		}
		if(find(1) == find(n)) break;
	}
	printf("%d\n", ans);
	return 0;
}

7 - 4 数据结构设计

解题思路 1

维护一个单调递增的单调队列以及一个单调递减的单调队列,可以 O ( 1 ) O(1) O(1) 查询当前区间的最大值和最小值。对所有数据取反可以通过一个标记实现。稍微具体解释一下“区间”的含义,将所有 I I I 操作插入到序列中的元素按照时间先后顺序排成一个数列 A A A,不难说明,每次查询最值时,“数据结构” 中的元素一定是 A A A 数列中的某个区间。

代码实现 1

#include <cstdio>
#include <algorithm>
//#include <queue>
using namespace std;

const int maxn = 400000 + 6;

struct deque {
	int mem[maxn];
	int L, R;
	void push_back(int x) {
		mem[++ R] = x;
	}
	void pop_front() {
		if(L < R) {
			L ++;
		}
	}
	void pop_back() {
		if(L < R) {
			R --;
		}
	}
	int front() {
		return mem[L + 1];
	}
	int back() {
		return mem[R];
	}
	bool empty() {
		return L >= R; 
	}
};

deque UP, DOWN;
int flag = 0, A[maxn], acnt, bcnt;

void insert(int id) {
	while(!UP.empty() && A[UP.back()] >= A[id]) UP.pop_back();
	UP.push_back(id);
	while(!DOWN.empty() && A[DOWN.back()] <= A[id]) DOWN.pop_back();
	DOWN.push_back(id);
}

int MAX() {
	return A[DOWN.front()];
}
int MIN() {
	return A[UP.front()];
}

int main() {
	int m; scanf("%d", &m);
	int first = 1;
	for(int i = 1; i <= m; i ++) {
		char op[3]; scanf("%s", op);
		if(op[0] == 'I') {
			int x; scanf("%d", &x);
			if(flag) {
				x = -x;
			}
			A[++ acnt] = x; /// acnt 指向最后一个位置 
			insert(acnt);
		}else if(op[0] == 'D') {
			if(bcnt < acnt) {
				bcnt ++; /// 已经被删除的部分的最后一个元素 
				while(!UP.empty() && UP.front() <= bcnt) UP.pop_front();
				while(!DOWN.empty() && DOWN.front() <= bcnt) DOWN.pop_front();
			}
		}else if(op[0] == 'R') {
			flag ^= 1;
		}else {
			/// MAX
			if(bcnt < acnt) {
				 /// with number
				if(!first) putchar('\n');
			    if(flag) {
			    	printf("%d", -MIN());	
			    }else {
			    	printf("%d", MAX());
			    }
			    first = 0;
			}
		}
	}
	return 0;
}

解题思路 2

使用双端队列维护当前“数据结构”中的数据序列,用一个 map 记录 “数据结构” 中每种数值的出现次数 或者 multiset 维护 “数据结构” 中的数据集合。

代码实现 2

#include <cstdio>
#include <map>
#include <queue>
#include <cstring>
using namespace std;

int flag = 0; /// positive
map<int, int> cnt;
queue<int> q;

int main() {
	int M; scanf("%d", &M);
    int nfst = 0;
	while(M --) {
		char op[3]; scanf("%s", op);
		if(op[0] == 'I') {
			int x; scanf("%d", &x);
			if(flag) {
				x = - x;
			}
			q.push(x);
			cnt[x] ++;
		}else if(op[0] == 'D') {
			if(!q.empty()) {
				/// 删除队首 
				int x = q.front(); q.pop();
				cnt[x] --;
				if(cnt[x] == 0) {
					cnt.erase(x);
				}
			}
		}else if(op[0] == 'R') {
			flag ^= 1;
		}else if(op[0] == 'M') {
			if(!q.empty()) {
                if(nfst) {
                    putchar('\n');
                }
				if(flag) {
					/// neg
					printf("%d", -cnt.begin()->first);
				}else {
					printf("%d", cnt.rbegin()->first);
				}
                nfst = 1;
			}
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值