JLU第二次数据结构上机实验解题报告

T1:数列查询

题目

已知数列的通项公式为:

f(n) = f(n-1)*11/10,f[1]=10.

通项从左向右计算,*和/分别表示整数乘法和除法。 现在,要多次查询数列项的值。

INPUT

第1行,1个整数q,表示查询的次数, 1≤q≤10000. 第2至q+1行,每行1个整数i,表示要查询f(i)的值。

OUTPUT

q行,每行1个整数,表示f(i)的值。查询的值都在32位整数范围内。

Sample

INPUT

3
1
2
3

OUTPUT

10
11
12

Time limit

10ms

Memory limit

1MB

Data range

1≤q≤10000

Solution

读完题先看数据范围,1≤q≤10000,但并没有给数i的范围,但在输出格式内给出了查询的值都在32位整数范围内这一界定范围,我们可以通过一个简单的打表来找到i的最大值。

for(int i=2;i<=100000;i++){
		f[i] = f[i - 1] * 11 / 10;
		if(f[i] < 0)break;
		printf("%d %d\n",i,f[i]);
	}

输出为

177 119388179
178 131326996
179 144459695
180 158905664
181 174796230
182 192275853
183 211503438

得出i的数据范围是1-183左右

在得出i的范围后,就可以考虑解法了。

首先第一能想到的就是直接1-i暴力解出f(i),时间复杂度最坏为O(1830000),正常来说很稳

然而。。

TIME LIMIT: 10 ms

时间限制十分严格,任何常数大一点的操作都有可能T掉,更不要说暴力了

考虑用一个数组来存下f(1~183)

空间复杂度为183个int,不会超过空间限制

这样预处理复杂度只有183,询问复杂度只有O(1),在10ms的时间限制下是可以通过的

赛时AC代码

#include <bits/stdc++.h>

using namespace std;

int tot;
int f[100005];

int main(){
	int q;
	scanf("%d",&q);
	f[1] = 10;
	for(int i=2;i<=100000;i++)
		f[i] = f[i - 1] * 11 / 10;
	while(q--){
		int x;scanf("%d",&x);
		printf("%d\n",f[x]);
	}
	return 0;
}

但即使是算法正确,仍然有人不幸T掉这题,这是为什么呢?

这里就要引入常数的概念了

程序的任何一条语句,都要消耗一定的时间,将这一时间称之为常数。

有些算法虽然时间复杂度相同,但不同代码常数却不尽相同

例如如下两段代码

	for(int i=1;i<=n;i++){
		x += 2;
	}
	for(int i=1;i<=n;i++){
		x += 1;
		x += 1;
	}

虽然效果一摸一样,时间复杂度也是O(n),但第二个程序相当于执行了两次加法,第一个只执行了一次。

在数据规模小的时候,是看不出来的

但在数据规模极大的时候,两个程序的耗时就会出现一定的差距

这也正是有时正解也不过的原因,代码常数过大

下面给出一些常数比对

printf/scanf 比 cin/cout 快

位运算 比 加减乘除 快

等等

感兴趣的可以自行搜索

 

T2:稀疏矩阵之和

题目

矩阵A和B都是稀疏矩阵。请计算矩阵的和A+B.如果A、B不能做和,输出“Illegal!”

INPUT

矩阵的输入采用三元组表示,先A后B。对每个矩阵:

第1行,3个整数N、M、t,用空格分隔,分别表示矩阵的行数、列数和非0数据项数。

第2至t+1行,每行3个整数r、c、v,用空格分隔,表示矩阵r行c列的位置是非0数据项v, v在32位有符号整型范围内。三元组默认按行列排序。

OUTPUT

矩阵A+B,采用三元组表示,默认按行列排序,非零项也在32位有符号整型范围内。

Sample

INPUT

10 10 3
2 2 2
5 5 5
10 10 20
10 10 2
2 2 1
6 6 6

OUTPUT

10 10 4
2 2 3
5 5 5
6 6 6
10 10 20

Time limit

100ms

Memory limit

10MB

Data range

10≤N、M≤50000,t≤min(N,M)

Solution

第一眼看的时候,我以为是教科书上的十字链表法存矩阵,但写了一半发现完全没有必要,用类似存图的手法来存该矩阵就可以,但需要注意的是如果加后矩阵该位置变成0了,需要从链表中删除。更需要注意的是,题目中“illegal!”中叹号是中文叹号,而数据中是英文叹号,这东西防不胜防,不用复制粘贴大法也还有“YE5”“N0”等着你,只能说多多注意

赛时AC代码

#include <bits/stdc++.h>

using namespace std;

struct Node{
	int row,col;
	long long val;
	Node* rig;
};

Node* rhead[100005];

int n1,m1,t1;
int n2,m2,t2;
int sum;

int main(){
	scanf("%d%d%d",&n1,&m1,&t1);
	for(int i=1;i<=100000;i++){
		rhead[i] = (Node*)malloc(sizeof(Node));
		rhead[i] -> row = i;
		rhead[i] -> col = -1;
		rhead[i] -> val = 0;
		rhead[i] -> rig = NULL;
	}
	for(int i=1;i<=t1;i++){
		int r,c,val;
		scanf("%d%d%d",&r,&c,&val);
		Node* it = (Node*)malloc(sizeof(Node));
		it -> row = r;
		it -> col = c;
		it -> val = val;
		Node *now = rhead[r];
		Node *las = NULL;
		if(now -> rig == NULL)now -> rig = it,it -> rig = now,sum++;
		else {
			las = now;
			now = now -> rig;
			while(now -> col < c && now -> rig != rhead[r])las = now,now = now -> rig;
			if(now -> col == c)now -> val += val;
			else if(now -> rig == rhead[r] && now -> col < c)now -> rig = it,it -> rig = rhead[r],sum++;
			else las -> rig = it,it -> rig = now,sum++;
		}
	}
	scanf("%d%d%d",&n2,&m2,&t2);
	if(n1 != n2 || m1 != m2)return printf("Illegal!"),0;
	for(int i=1;i<=t2;i++){
		int r,c,val;
		scanf("%d%d%d",&r,&c,&val);
		Node* it = (Node*)malloc(sizeof(Node));
		it -> row = r;
		it -> col = c;
		it -> val = val;
		Node *now = rhead[r];
		Node *las = NULL;
		if(now -> rig == NULL)now -> rig = it,it -> rig = now,sum++;
		else {
			las = now;
			now = now -> rig;
			while(now -> col < c && now -> rig != rhead[r])las = now,now = now -> rig;
			if(now -> col == c){
				now -> val += val;
				if(now -> val == 0){
					sum--;
					las -> rig = now -> rig;
				}
			}
			else if(now -> rig == rhead[r] && now -> col < c)now -> rig = it,it -> rig = rhead[r],sum++;
			else las -> rig = it,it -> rig = now,sum++;
		}
	}
	printf("%d %d %d\n",n1,m1,sum);
	for(int i=1;i<=n1;i++){
		if(rhead[i] -> rig == NULL)continue;
		for(Node* j=rhead[i]->rig;j != rhead[i];j=j->rig){
			if(j -> val == 0)continue;
			printf("%d %d %lld\n",j -> row,j -> col,j -> val);
		}
	}
	return 0;
}

T3:文字编辑

题目

一篇文章由n个汉字构成,汉字从前到后依次编号为1,2,……,n。 有四种操作:

A i j表示把编号为i的汉字移动编号为j的汉字之前;

B i j表示把编号为i的汉字移动编号为j的汉字之后;

Q 0 i为询问编号为i的汉字之前的汉字的编号;

Q 1 i为询问编号为i的汉字之后的汉字的编号。

规定:1号汉字之前是n号汉字,n号汉字之后是1号汉字。

INPUT

第1行,1个整数T,表示有T组测试数据

随后的每一组测试数据中,第1行两个整数n和m,用空格分隔,分别代表汉字数和操作数,第2至m+1行,每行包含3个常量s、i和j,用空格分隔,s代表操作的类型,若s为A或B,则i和j表示汉字的编号,若s为Q,i代表0或1,j代表汉字的编号。

OUTPUT

若干行,每行1个整数,对应每个询问的结果汉字编号。

Sample

INPUT

1 
9999 4 
B 1 2  
A 3 9999
Q 1 1
Q 0 3

OUTPUT

4
9998

Time limit

1000ms

Memory limit

2MB

Data range

1≤T≤9999,2≤n≤9999,1≤m≤9999

Solution

观察发现所有操作只改变前后关系,并不改变其数值,双向链表模拟操作即可

赛时AC代码

#include <bits/stdc++.h>

using namespace std;

int bef[100005];
int aft[100005];

int main(){
	int T;scanf("%d",&T);
	while(T--){
		int n,m;scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
			bef[i] = i - 1,aft[i] = i + 1;
		bef[1] = n;
		aft[n] = 1;
		while(m--){
			char ch[10];
			scanf("%s",ch);
			if(ch[0] == 'A'){
				int x,y;scanf("%d%d",&x,&y);
				aft[bef[x]] = aft[x];
				bef[aft[x]] = bef[x];
				bef[x] = bef[y];
				aft[bef[y]] = x;
				aft[x] = y;
				bef[y] = x;
			}
			else if(ch[0] == 'B'){
				int x,y;scanf("%d%d",&x,&y);
				aft[bef[x]] = aft[x];
				bef[aft[x]] = bef[x];//a y x b
				aft[x] = aft[y];
				bef[aft[y]] = x;
				aft[y] = x;
				bef[x] = y;
			}
			else {
				int opt;scanf("%d",&opt);
				int x;scanf("%d",&x);
				if(opt == 1)printf("%d\n",aft[x]);
				else printf("%d\n",bef[x]);
			}
		}
	}
	return 0;
}

T4:幸福指数

题目

人生中哪段时间最幸福?幸福指数可能会帮你发现。幸福指数要求:对自己每天的生活赋予一个幸福值,幸福值越大表示越幸福。一段时间的幸福指数就是:这段时间的幸福值的和乘以这段时间的幸福值的最小值。幸福指数最大的那段时间,可能就是人生中最幸福的时光。

INPUT

第1行,1个整数n,表示要考察的天数。

第2行,n个整数Hi,用空格分隔,Hi表示第i天的幸福值。

OUTPUT

第1行,1个整数,表示最大幸福指数。

第2行,2个整数l和r,用空格分隔,表示最大幸福指数对应的区间[l,r]。如果有多个这样的区间,输出最长最左区间。

Sample

INPUT

7
6 4 5 1 4 5 6

OUTPUT

60
1 3

Time limit

100ms

Memory limit

64MB

Data range

1≤n≤100000 0≤Hi≤1000000

Solution

本题与 POJ-2559 Largest Rectangle in a Histogram除了数值算法不同以外几乎相同,然而我没看出来,赛时瞎写了暴力,赛后立即顿悟。

本题的解法是单调栈

先来复习一下单调栈

定义:栈内元素单调递增或者单调递减的栈

性质:

1.单调栈里的元素具有单调性

2.元素加入栈前,会将栈顶所有破坏单调性的元素都删除

3.利用单调栈可以找到元素向左遍历第一个比他小/大的元素

4.时间复杂度O(n)

再来看本题,幸福指数的计算需要幸福值的和和幸福值的最小值,于是我们可以做一个结构体,里面存区间和与区间最小值,当当前要插入的数比栈顶区间最小值小时,将这些弹出来的结构体捏成一个新的结构体再放回去,期间不断计算当前的幸福指数并更新ans即可得出答案。

赛后AC代码

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

#define int long long

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

struct Node{
	int high,wide;
	int l,r;
}stac[100005];

Node NewNode(int x,int y,int ll,int rr){
	Node v;
	v.high = x,v.wide = y;
	v.l = ll,v.r = rr;
	return v;
}

int n,T;
int a[100005];
int top;
int ans,mxl,mxr;

signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++){
		if(i != n + 1)a[i] = read();
		else a[i] = 0;
		if(a[i] >= stac[top].high)stac[++top] = NewNode(a[i],a[i],i,i);
		else {
			int wid = 0,l,r = stac[top].r;
			while(a[i] < stac[top].high && top){
				wid += stac[top].wide;
				l = stac[top].l;
				if(ans < stac[top].high * wid){
					ans = stac[top].high * wid;
					mxl = l,mxr = r;
				} else if(ans == stac[top].high * wid){
					if(mxr - mxl < r - l)mxl = l,mxr = r;
					else if(mxr - mxl == r - l && l < mxl)mxl = l,mxr = r;
				}
				top--;
			}
			Node t = NewNode(a[i],wid + a[i],l,i);
			if(ans < t.wide * t.high){
				ans = t.wide * t.high;
				mxl = l,mxr = i;
			} else if(ans == t.wide * t.high){
				if(mxr - mxl < i - l)mxl = l,mxr = i;
				else if(mxr - mxl == i - l && l < mxl)mxl = l,mxr = i;
			}
			stac[++top] = t;
		}
	}
	if(!mxl && !mxr){printf("0\n1 %d",n);}
	else printf("%lld\n%lld %lld",ans,mxl,mxr);
	return 0;
}

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值