数据结构大作业实验报告

2023-2024《数据结构》大作业报告

A. 岛屿链接

解题思路

  1. 将所有初始的完整海岛所包含的格子进行统一编号,并统计每个海岛的大小。
  2. 尝试将每一块海变为陆地,并用新得到的陆地的大小更新答案。

Code

#include<iostream>
#include<cstdio>

using namespace std;

int read() {
	int x = 0, f = 1; char ch;
	ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') f = -f; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}

int nx[4] = {1, -1, 0, 0};
int ny[4] = {0, 0, 1, -1};

int n, map[1005][1005], cnt;

int sum[1000005];

void update(int x, int y, int k) {
	if(map[x][y] != 1) return;
	sum[k]++;
	map[x][y] = k;
	for(int i = 0; i <= 3; i++)
		update(x + nx[i], y + ny[i], k);
}

int query(int x, int y) {
	if(map[x][y]) return sum[map[x][y]];
	int r[4], tmp = 1;
	for(int i = 0; i <= 3; i++)
		r[i] = map[x + nx[i]][y + ny[i]];
	tmp += sum[r[0]];
	if(r[1] != r[0]) tmp += sum[r[1]];
	if(r[2] != r[0] && r[2] != r[1]) tmp += sum[r[2]];
	if(r[3] != r[0] && r[3] != r[1] && r[3] != r[2]) tmp += sum[r[3]];
	return tmp;
}

int main() {
	n = read();
	
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			map[i][j] = read();
	
	cnt = 1;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			update(i, j, ++cnt);
	
	int ans = 0;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			ans = max(ans, query(i, j));
	
	printf("%d", ans);
	return 0;
}

B. 小蓝鲸订酒店

解题思路

题目大意是让我们在一个动态序列中维护第x/m大的元素。

考虑使用两个堆实现,一个大根堆存储前x/m个元素,一个小根堆存储后(x-m)/m大的元素,每次插入后通过移动堆顶元素来维护两个堆的大小。每次更新后大根堆堆顶元素即为所求。

Code

#include<iostream>
#include<cstdio>
#include<queue>

using namespace std;

int read() {
	int x = 0, f = 1; char ch;
	ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') f = -f; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}

//priority_queue< int, vector<int>, greater<int> > q1;
//
//priority_queue< int > q2;

class heap {
private:
	int heap[1000001], heap_length, tot;
	
public:
	void push(int t) {
		int now, next;
		heap[++heap_length] = t;
		now = heap_length;
		while(now > 1)
		{
			next = now / 2;
			if(heap[now] >= heap[next]) return;
			swap(heap[now], heap[next]);
			now = next;
		}
	}
	
	int pop() {
		int next, tot, now;
		tot = heap[1];
		heap[1] = heap[heap_length--];
		now = 1;
		while(2 * now <= heap_length)
		{
			next = 2 * now;
			if(next < heap_length && heap[next + 1] < heap[next])
				next++;
			if(heap[now] <= heap[next]) return tot;
			swap(heap[now], heap[next]);
			now = next;
		}
		return tot;
	}
	
	int top() {
		return heap[1];
	}
	
	bool empty() {
		return !heap_length;
	}
	
	int size() {
		return heap_length;
	}
}q1, q2;

int m, n;

int main() {
	m = read();
	n = read();
	for(int i = 1; i <= n; i++) {
		int tmp, cnt;
		tmp = read();
		cnt = (i + m - 1) / m;
		if(q1.empty() || tmp > q1.top()) q1.push(tmp);
		else q2.push(-tmp);
		if(q1.size() < cnt) q1.push(-q2.top()), q2.pop();
		if(q1.size() > cnt) q2.push(-q1.top()), q1.pop();
		printf("%d ", q1.top());
	}
	return 0;
}

C. 小蓝鲸乘坐火车

解题思路

显然是一个带限制的单源最短路的题目。

和普通最短路相比区别在于在更新时要考虑火车线路的维修。即每条路径更新所用时间为路径时间加剩余维护时间。

query()函数来计算路径用时:

int query(int t, int k) {
	int p1 = e[k].p1, p2 = e[k].p2, w = e[k].w;
	if(t + w < p1 || t >= p2) return t + w;
	return p2 + w;
}

最短路部分用Dijkstra算法即可。

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define INF 2147483647
#define M(a,b) (node){(a),(b)}
#define re register
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define qwq printf("qwq\n");

using namespace std;

long long read() {
	register long long x = 0,f = 1;register char ch;
	ch = getchar();
	while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
	while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
	return x * f;
}

int n,m,x,y,z,p1,p2,s,cnt,d[100005],dis[100005],pd[100005];

struct edge {
	int to,nex,w,p1,p2;
}e[500005];

struct node {
	int k,dis;
	bool operator < (const node &x) const {
		return x.dis < dis;
	}
};

priority_queue<node> que;

void add(int x,int y,int z,int p1,int p2) {
	e[++cnt].to = y;
	e[cnt].w = z;
	e[cnt].nex = d[x];
	e[cnt].p1 = p1;
	e[cnt].p2 = p2;
	d[x] = cnt;
}

int query(int t, int k) {
	int p1 = e[k].p1, p2 = e[k].p2, w = e[k].w;
	if(t + w < p1 || t >= p2) return t + w;
	return p2 + w;
}

void dij() {
	memset(dis,0x3f,sizeof(dis));
	dis[1] = 0; que.push(M(1,dis[1])); pd[1] = 1;
	while(!que.empty()) {
		int u = que.top().k; que.pop();
		if(!pd[u]) continue; pd[u] = 0;
		for(int i = d[u]; i; i = e[i].nex) {
			int v = e[i].to;
			if(dis[v] > query(dis[u], i)) {
				dis[v] = query(dis[u], i);
				pd[v] = 1; que.push(M(v,dis[v]));
			}
		}
	}
}

int main() {
	n = read(); m = read();
	for(int i = 1; i <= m; i++) {
		x = read(); y = read(); z = read(); p1 = read(); p2 = read();
		add(x,y,z,p1,p2);
		add(y,x,z,p1,p2);
	}
	dij();
	printf("%d", dis[n] + 1);
    return 0;
}

D. 小蓝鲸买纪念品

解题思路

对于每个商品需要维护三项信息:是否存在、最近购买时间以及购买数量。

对于每次顾客购买事件,先判断目标商品是否存在,存在则更新最近购买事件以及购买数量,不存在则输出-1。

对于每次进货事件,先判断目标商品是否存在,存在则不进行任何操作,不存在则按照给出的策略进货。

对于需要剔除商品的情况,朴素算法中可以遍历每一个商品并更新目标,时间复杂度为O(mk),其中k为商品种类。该算法时间复杂度较高,无法满足题目要求。

以下为朴素算法代码

#include<iostream>
#include<cstdio>

using namespace std;

int read() {
	int x = 0, f = 1; char ch;
	ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') f = -f; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}

int n, m, x, y, z, t, opt, vis[100005], tim[100005], cnt[100005];

void sell(int now) {
	if(!vis[x]) {
		printf("-1\n");
		return;
	}
	cnt[x]++;
	tim[x] = now;
}

void del(int x) {
	printf("%d\n", x);
	vis[x] = 0;
	cnt[x] = 0;
}

void buy(int now) {
	if(vis[y]) return;
	vis[y] = now;
	tim[y] = now;
	cnt[y] = 0;
	t++;
	if(t > n) {
		t--;
		if(z == 1) {
			int tmpn, tmpmin = 2147483647;
			for(int i = 0; i <= 100000; i++)
				if(vis[i] && tim[i] < tmpmin && i != y)
					tmpmin = tim[i], tmpn = i;
			del(tmpn);
		}
		else {
			int tmpn = 0, tmpmin = 2147483647;
			for(int i = 0; i <= 100000; i++)
				if(vis[i] && i != y)
					if(cnt[i] < tmpmin || (cnt[i] == tmpmin && tim[i] < tim[tmpn]))
						tmpmin = cnt[i], tmpn = i;
			del(tmpn);
		}
	}
}

int main() {
	n = read();
	m = read();
	for(int i = 1; i <= m; i++) {
		opt = read();
		if(opt == 1) x = read(), sell(i);
		else y = read(), z = read(), buy(i);
	}
	for(int i = 0; i <= 100000; i++)
		if(vis[i])
			printf("%d ", i);
	return 0;
}

考虑对时间复杂度进行优化。

可以使用堆来维护目前存在的商品,使下一个要剔除的商品为堆顶元素。

由于有两种不同的进货策略,我们需要维护两个不同的堆,其中一个堆按照策略一维护堆内元素,另一个堆按照策略二维护堆内元素。为简化代码,在堆的维护过程中我们使用compare()函数进行元素之间的比较,并给compare()函数传入参数z来区分按照哪种策略进行比较。

每当我们需要按照某种策略剔除商品是,我们只需取出相应堆的堆顶元素,即为剔除目标。注意,在事件过程中,堆内原有元素会因商品相关数据的改变而失效,当我们选择剔除目标时,应将堆顶失效元素(当堆顶元素所记录的商品数据与当前商品数据不符时,我们认为这个元素是失效的)全部舍弃,直到取到有效元素为止。

Code

#include<iostream>
#include<cstdio>

using namespace std;

int read() {
	int x = 0, f = 1; char ch;
	ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') f = -f; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}

int n, m, x, y, z, t, opt, vis[100005], tim[100005], cnt[100005];

struct node{
	int x, tim, cnt;
};

bool compare(node a, node b, int z) {
	if(z == 1) return a.tim < b.tim;
	return a.cnt == b.cnt ? a.tim < b.tim : a.cnt < b.cnt;
}

class heap {
private:
	int  heap_length, tot;
	node heap[200005];
	
public:
	void push(int t, int tim, int cnt, int z) {
		int now, next;
		heap[++heap_length].x = t;
		heap[heap_length].tim = tim;
		heap[heap_length].cnt = cnt;
		now = heap_length;
		while(now > 1)
		{
			next = now / 2;
			if(compare(heap[next], heap[now], z)) return;
			swap(heap[now], heap[next]);
			now = next;
		}
	}
	
	void pop(int z) {
		int next, now;
		heap[1] = heap[heap_length--];
		now = 1;
		while(2 * now <= heap_length)
		{
			next = 2 * now;
			if(next < heap_length && compare(heap[next + 1], heap[next], z))
				next++;
			if(compare(heap[now], heap[next], z)) return;
			swap(heap[now], heap[next]);
			now = next;
		}
	}
	
	node top() {
		return heap[1];
	}
	
	bool empty() {
		return !heap_length;
	}
	
	int size() {
		return heap_length;
	}
	
	void test() { //测试用函数 
		printf("test: ");
		for(int i = 1; i <= heap_length; i++)
			printf("(%d, %d, %d) ", heap[i].x, heap[i].tim, cnt[heap[i].x]);
		printf("\n");
	}
}q1, q2;

void sell(int now) {
	if(!vis[x]) {
		printf("-1\n");
		return;
	}
	cnt[x]++;
	tim[x] = now;
	q1.push(x, now, cnt[x], 1);
	q2.push(x, now, cnt[x], 2);
}

void del(int x) {
	printf("%d\n", x);
	vis[x] = 0;
	cnt[x] = 0;
}

void buy(int now) {
	if(vis[y]) return;
	t++;
	if(t > n) {
		t--;
		if(z == 1) {
			while(!vis[q1.top().x] || tim[q1.top().x] != q1.top().tim) q1.pop(1);
			del(q1.top().x);
			q1.pop(1);
		}
		else {
			while(!vis[q2.top().x] || tim[q2.top().x] != q2.top().tim) q2.pop(2);
			del(q2.top().x);
			q2.pop(2);
		}
	}
	vis[y] = now;
	tim[y] = now;
	cnt[y] = 0;
	q1.push(y, now, cnt[y], 1);
	q2.push(y, now, cnt[y], 2);
}

int main() {
	n = read();
	m = read();
	for(int i = 1; i <= m; i++) {
		opt = read();
		if(opt == 1) x = read(), sell(i);
		else y = read(), z = read(), buy(i);
	}
	for(int i = 0; i <= 100000; i++)
		if(vis[i])
			printf("%d ", i);
	return 0;
}
posted @ 2024-01-18 21:39  AuroraPolaris  阅读(22)  评论(0编辑  收藏  举报