数据结构大作业实验报告
2023-2024《数据结构》大作业报告
A. 岛屿链接
解题思路
- 将所有初始的完整海岛所包含的格子进行统一编号,并统计每个海岛的大小。
- 尝试将每一块海变为陆地,并用新得到的陆地的大小更新答案。
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;
}