数据结构第二次上机

7-1 数列查询 (100 分)

已知数列的通项公式为:

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

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

输入格式:

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

输出格式:

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

输入样例:

在这里给出一组输入。例如:

3
1
2
3

输出样例:

在这里给出相应的输出。例如:

10
11
12

 解题思路:

在最开始我的思路是将计算过的f(n)存储下来,没有计算过的就递归计算,这样发现只能过第一个,第二个案例始终过不了。而这道题的时间限制只有10ms,所以用递归算的话肯定会超时,后面经过老师指点,发现这道题考查我们的其实是打点,把没有超过整型的项都存储到一个数组里(可以计算知道在大约300项左右就已经超过整型了),因此可以一开始直接用循环计算,后面求f(i)的值就是O(1)的时间复杂度,这样刚好过第二个测试点。

#include<iostream>
using namespace std;
int main(void){
	int sum[20000];
	int *num; 
	int max=0;
	int n,i;
	scanf("%d",&n);
	num=new int[n];
	for(i=0;i<n;i++){
		scanf("%d",&num[i]);
		if(num[i]>max)max=num[i];
	}
	sum[1]=10;
	for(i=2;i<=max;i++){
		sum[i]=sum[i-1]*11/10;
	}
	for(i=0;i<n;i++){
		printf("%d\n",sum[num[i]]);
	}
}

 

7-2 稀疏矩阵之和 (100 分)

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

输入格式:

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

第1行,3个整数N、M、t,用空格分隔,分别表示矩阵的行数、列数和非0数据项数,10≤N、M≤50000,t≤min(N,M).

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

输出格式:

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

输入样例:

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

输出样例:

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

 解题思路:

这道题老师在上课之前让我们提前预习过了,所以存储结构十分明了,即用一个结构体存储行号,列号,以及该位置的值。后面最主要的就是如何做到矩阵加法,这里要注意的小细节是在矩阵相加某元素为0时在结果中该元素不会显示。首先我开了两个结构体指针,一个开的很大,不仅用于存储第一个矩阵同时也用于存储结果的矩阵,第二个则根据矩阵大小开多大内存。在读取完第一个矩阵后,我先把第一个矩阵用sort排序,再把相关元素的行数和列数用向量存储,并存在map里,对应唯一的位置,在读取第二个矩阵的同时,先搜索第一个矩阵是否有相同位置的元素,如果有直接相加,没有的话放到第一个矩阵后面,整体加完后再进行一边排序。注:多用scanf和printf,用cin与cout,很可能会超时。

代码:

#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
struct M {
	int a;
	int b;
	int data;
};
map<vector<int>, int> mp;
vector<int> *v;
M* tab[2];
int cmp(M& a, M& b) {
	if (a.a < b.a)return 1;
	else if (a.a > b.a)return 0;
	else {
		if (a.b < b.b)return 1;
		else return 0;
	}
}
int main(void) {
	int m, n, t;
	int x, y, s;
	int i;
	int sum = 0;
	int flag = 0;
    scanf("%d%d%d",&m,&n,&t);
	tab[0] = new M[50000];
	for (i = 0; i < t; i++) {
		scanf("%d%d%d", &tab[0][i].a, &tab[0][i].b, &tab[0][i].data);
		if (tab[0][i].a > m || tab[0][i].b > n|| tab[0][i].a<=0|| tab[0][i].b<=0) {
			flag = 1;
		}
	}
	sort(&tab[0][0], &tab[0][t], cmp);
	for (i = 0; i < t; i++) {
		v = new vector<int>;
		(*v).push_back(tab[0][i].a);
		(*v).push_back(tab[0][i].b);
		mp[(*v)] = i+1;
		sum++;
		delete v;
	}
    scanf("%d%d%d",&x,&y,&s);
	tab[1] = new M[s+1];
	for (i = 0; i < s; i++) {
		scanf("%d%d%d", &tab[1][i].a, &tab[1][i].b, &tab[1][i].data);
		if (tab[1][i].a > x || tab[1][i].b > y|| tab[1][i].a<=0|| tab[1][i].b <=0) {
			flag = 1;
		}
		v = new vector<int>;
		(*v).push_back(tab[1][i].a);
		(*v).push_back(tab[1][i].b);
		if (!mp[(*v)]) {
			tab[0][sum].a = tab[1][i].a;
			tab[0][sum].b = tab[1][i].b;
			tab[0][sum].data = tab[1][i].data;
			sum++;
		}
		else {
			tab[0][mp[(*v)] - 1].data += tab[1][i].data;
			if (tab[0][mp[(*v)] - 1].data == 0)sum--;
		}
		delete v;
	}
	sort(&tab[0][0], &tab[0][sum], cmp);
	if (x != m || y != n||flag) {
		printf("Illegal!"); return 0;
	}
	printf("%d %d %d\n", m, n, sum);
	for (i = 0; i < sum; i++) {
		if(tab[0][i].data)printf("%d %d %d\n", tab[0][i].a, tab[0][i].b, tab[0][i].data);
	}
}

7-3 文字编辑 (100 分)

一篇文章由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号汉字。

输入格式:

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

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

输出格式:

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

输入样例:

在这里给出一组输入。例如:

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

输出样例:

在这里给出相应的输出。例如:

4
9998

 解题思路:

读完题后就明白这道题想要考查我们的是双向循环链表,我选择的是用动态链表实现,在一个结构体中存储位置,前驱以及后继,又因为这道题内存限制为2MB,所以要一开始就生成从1到9999的链表,再用一个数组存储每个汉字数对应的指针,这样在查找的时候时间复杂度仅为O(1),后面只要把摘下,插入这些细节注意一下就可以了,对了,还是内存限制,不要用数组存储询问结果要直接输出,否则内存会爆。

代码:

#include<iostream>
using namespace std;
struct w {
	int data;
	struct w* next;
	struct w* before;
};
typedef struct w* wa;
wa tab[10002];
int main(void) {
	int n;
	int f, l;
	int num, op;
	int i;
	int ie = 1;
	char ch;
	int k = 0;
	wa head;
	wa p, q, p0;
	p = p0 = head = NULL;
	for (ie = 1; ie <= 9999; ie++) {
		p = new struct w;
		p->data = ie;
		p->before = p0;
		p->next = head;
		tab[ie] = p;
		if (ie == 1) {
			head = p;
		}
		else {
			p0->next = p;
		}
		p0 = p;
	}
	head->before = p;
	scanf("%d", &n);
	for (i = 0; i < n; i++) {
		scanf("%d%d", &num, &op);
		for (ie = 1; ie <= num; ie++) {
			if (ie < num)tab[ie]->next = tab[ie + 1];
			else {
				tab[ie]->next = tab[1];
			}
			if (ie == 1)tab[ie]->before = tab[num];
			else {
				tab[ie]->before = tab[ie - 1];
			}
		}
		k = 0;
		for (int j = 0; j < op; j++) {
			ch = getchar();
			while (ch != 'A' && ch != 'B' && ch != 'Q') {
				ch = getchar();
			}
			if (ch == 'A') {
				scanf("%d%d", &f, &l);
				p = tab[f];
				q = tab[l];
				p->next->before = p->before;
				p->before->next = p->next;
				q->before->next = p;
				p->before = q->before;
				q->before = p;
				p->next = q;
			}
			else if (ch == 'B') {
				scanf("%d%d", &f, &l);
				p = tab[f];
				q = tab[l];
				p->next->before = p->before;
				p->before->next = p->next;
				q->next->before = p;
				p->next = q->next;
				q->next = p;
				p->before = q;
			}
			else if (ch == 'Q') {
				scanf("%d%d", &f, &l);
				if (f == 0) {
					printf("%d\n", (tab[l]->before)->data);
				}
				else printf("%d\n", (tab[l]->next)->data);
			}
		}
	}
}

7-4 幸福指数 (100 分)

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

输入格式:

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

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

输出格式:

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

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

输入样例:

在这里给出一组输入。例如:

7
6 4 5 1 4 5 6

输出样例:

在这里给出相应的输出。例如:

60
1 3

 解题思路:

在上次考过单调队列后我就在想这次会考单调栈,但遇到这道题我不太清楚怎么使用。在经过老师同学讲解以及结合之前单调队列的方法,我发现这两者的思路很像,因为变量很多,尤其这道题多了乘于最小值,所以应该先假设一个定量,假设为ai,要求以ai为中心的幸福指数,就需要知道两边第一个小于ai的数,确定以ai为最小数的范围,这样可以依次把i从1到n的幸福指数求出来再比较最大值,而求一个数左边或右边第一个小于它的数就是单调栈的一个经典应用,这样用单调栈就变得顺理成章了。又因为是最长最左区间,所以我们在判断大小时对等于max的情况要进行特判,比较区间长度以及相对位置。

代码:

#include<iostream>
#include<stack>
#include<vector>
using namespace std;
stack<int> fs;
stack<int> ls;
vector<int>* sk;
long long int* sum;
long long int* num;
int main() {
    int n;
    int i;
    long long int maxs = 0;
    long long int nows;
    int f_number, l_number;
    int len=0;
    int maxlen=0;
    scanf("%d", &n);
    sum = new long long int[n + 1];
    num = new long long int[n + 1];
    sk = new vector<int>[n + 2];
    num[0]=num[n+1]=-1;
    sum[0] = 0;
    for (i = 1; i <= n; i++) {
        scanf("%lld", &sum[i]);
        num[i] = sum[i];
        sum[i] += sum[i - 1];
    }
    fs.push(0);
    for (i = 1; i <= n; i++) {
        while (!fs.empty() && num[fs.top()] >= num[i]) {
            fs.pop();
        }
        sk[i].push_back(fs.top());
        fs.push(i);
    }
    ls.push(n + 1);
    for (i = n; i >= 1; i--) {
        while (!ls.empty() && num[ls.top()] >= num[i]) {
            ls.pop();
        }
        sk[i].push_back(ls.top() - 1);
        ls.push(i);
    }
    for (i = 1; i <= n; i++) {
        nows = (sum[sk[i][1]] - sum[sk[i][0]]) * num[i];
        len=sk[i][1]-sk[i][0];
        if (nows > maxs) {
            maxs = nows;
            f_number = sk[i][0] + 1;
            l_number = sk[i][1];
            maxlen=len;
        }
        else if(nows==maxs){
            if(len>maxlen){
                f_number = sk[i][0] + 1;
                l_number = sk[i][1];
                maxlen=len;
            }
            else if(len==maxlen){
                if(sk[i][1]<l_number){
                    f_number = sk[i][0] + 1;
                    l_number = sk[i][1];
                }
            }
        }
    }
    printf("%lld\n%d %d", maxs, f_number, l_number);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值