算法竞赛入门经典(第二版) | 例题4-5 追踪电子表格中的单元格 (UVa512,Spreadsheet Tracking,World Finals)(解法二)

本着清晰明了易懂可以水两篇 的理念,笔者将这道题分两次发布。这是第二种解法。


第一种解法传送门→解法一+提交网址


因为解法1中有详细关于题目和输入输出格式等的介绍,这里就不过多赘述了。

分析:

一些初学者做题的主要思路都是解法1:将题中每个可能的操作都考虑到,确保计算出所有可能的所有结果。
但解法2的思路相比上述有很大不同。
这个思路是将所有操作保存,然后对于每个查询重新执行每个操作,但不需要计算整个电子表格的变化,而只需关注所查询的单元格的位置变化。不仅更好写,且效率更高。
换句话说:我们的目标是解决问题,而不是为了写程序而写程序,同时应保持简单,而不是自己创造条件去展示编程技巧。

思路:

循环1:输入3变量,r,c,n,进入n个循环
循环2:输入操作类型s、若为交换,则输入4变量。若为其他,则输入行/列数,保存到数组。
还是循环2:输入num个坐标,进入函数,先解决交换(Easy),若为插入,且插入行在坐标行的前面,则最后输出坐标的行数+1,如果插入行等于坐标行,则返回GONE; 若为删除,且删除行在坐标行前面,则最后输出的坐标数减1;
返回,输出。

代码:


#include<stdio.h>
#include<string.h>
#define maxd 10000
 
struct Command{
	char c[5];//保存指令
	int r1, c1, r2, c2;//交换的两个坐标
	int a, x[20];// a 为当前指令行列变换操作次数 x[20] 存储的是相应指令集合
}cmd[maxd];
 
int r, c, n; //n为指令次数
 
int simulate(int *r0, int *c0){
	for(int i = 0; i < n; i++ ){
		if(cmd[i].c[0] == 'E'){//交换操作
			if(*r0 == cmd[i].r1 && *c0 == cmd[i].c1){
				*r0 = cmd[i].r2;
				*c0 = cmd[i].c2;
			}
			else if(*r0 == cmd[i].r2 && *c0 == cmd[i].c2){
				*r0 = cmd[i].r1;
				*c0 = cmd[i].c1;
			}
		}
		else if(cmd[i].c[0] != 'E'){
			int dr = 0,dc = 0;//记录每组此操作r0, c0坐标变换情况
			for(int j = 0; j < cmd[i].a; j++ ){
				int x = cmd[i].x[j];
				if(cmd[i].c[0] == 'I'){
					if(cmd[i].c[1] == 'R' && x <= *r0 ) dr++;//(需要插入当前行、列前面
					if(cmd[i].c[1] == 'C' && x <= *c0) dc++;//才会影响r0, c0)
				}
				else if(cmd[i].c[0] == 'D'){
					if(cmd[i].c[1] == 'R' && x == *r0) return 0;//删除当前行、列
					if(cmd[i].c[1] == 'C' && x == *c0) return 0;//时直接返回 0
					if(cmd[i].c[1] == 'R' && x < *r0) dr--;(同上注意删除不能是本行列)
					if(cmd[i].c[1] == 'C' && x < *c0) dc--;
				}
			}
			*r0 += dr;//每组指令执行完毕后 更新此时 坐标状况
			*c0 += dc;
		}
	}
	
	return 1;
}
int main()
	{
	//	freopen("C:\\Users\\zhangwei\\Desktop\\input.txt","r",stdin); 
		int r0, c0, q, kcase = 0;
		while(scanf("%d%d%d",&r,&c,&n) == 3 && r != 0){
			for(int i = 0; i < n; i++ ){
				scanf("%s",cmd[i].c);
				if(cmd[i].c[0] == 'E'){
					scanf("%d%d%d%d",&cmd[i].r1,&cmd[i].c1,&cmd[i].r2,&cmd[i].c2);
				}
				else{
					scanf("%d",&cmd[i].a);
					for(int j = 0; j < cmd[i].a; j++ ){
						scanf("%d",&cmd[i].x[j]);
					} 
				}
			}
			if(kcase++)
				printf("\n");
			printf("Spreadsheet #%d\n",kcase);
			
			scanf("%d",&q);
			while(q--){
				scanf("%d%d",&r0, &c0);
				printf("Cell data in (%d,%d) ", r0, c0);
				if(!simulate(&r0, &c0))
					printf("GONE\n");
				else
					printf("moved to (%d,%d)\n", r0, c0);	 
			}	
		}
		
		return 0;
	}

收获:

本题告诉我们,我们最终的目的是解题,而不是通盘考虑去展现算法技巧,所以我们只需关注与问题有关的变量,并将其过程化。


结语:
我们的目标是解决问题,而不是为了写程序而写程序,同时应保持简单,而不是自己创造条件去展示编程技巧。

发布了73 篇原创文章 · 获赞 61 · 访问量 4788

猜你喜欢

转载自blog.csdn.net/weixin_43899069/article/details/104388375