追踪电子表格中的单元格 Spreadsheet Tracking

2023-12-22 08:06:51

UVa512

参考书籍《算法竞赛入门经典》(第二版)

?

?思路一:先模拟操作,算出最后的电子表格,讲原始位置的值存在数组中,经过交换,插入,删除等操作后,在操作后的数组下标与数组当中所存在的值(0和原始位置)来查找存在与否或者移动后的位置。

?

#include <stdio.h>
#include <string.h>
#define maxd 100
#define BIG 10000
int r, c, n, d[maxd][maxd], d2[maxd][maxd], ans[maxd][maxd], cols[maxd];

void copy(char type, int p, int q)//拷贝对应行或者列上的数值
{
	if (type == 'R')//拷贝行,列动 行不动
	{
		for (int i = 1; i <= c; i++)
		{
			d[p][i] = d2[q][i];
		}
	}
	else//拷贝列, 列不动 行动
	{
		for (int i = 1; i <= r; i++)
		{
			d[i][p] = d2[i][q];
		}
	}
}

//删除函数
void del(char type)//type即为传进来的cmd[1]-->对行或者列操作
{
	memcpy(d2, d, sizeof(d));//将可能已经处理的d数组复制给d2数组
	int cnt = (type == 'R' ? r : c);//赋值给cnt  d数组对应的行数或者列数(取决于type)
	int cnt2 = 0;
	for (int i = 1; i <= cnt; i++)//赋值给cnt  d数组对应的行数或者列数(取决于type)
	{
		if (!cols[i])//由已经置为1的数组来判断操作,将没有置为1的行或者列重新排序
			copy(type, ++cnt2, i);//拷贝,注意++cnt2,即为从1开始
	}
	if (type == 'R')//更新行数或者列数,更新为执行指令后的值
		r = cnt2;
	else
		c = cnt2;
}

//插入函数
void ins(char type)//type即为传进来的cmd[1]-- > 对行或者列操作
{
	memcpy(d2, d, sizeof(d));//将可能已经处理的d数组复制给d2数组
	int cnt = (type == 'R' ? r : c);//赋值给cnt  d数组对应的行数或者列数(取决于type)
	int cnt2 = 0;
	for (int i = 1; i <= cnt; i++)//赋值给cnt  d数组对应的行数或者列数(取决于type)
	{
		if (cols[i])//由已经置为1的数组来判断操作,插入将置为1的行或者列,并数组整体重排
			copy(type, ++cnt2, 0);//插入,插入值为0,因为数组d/d2上的0行0列都是0,所以可以拷贝0行0列的数值0
		copy(type, ++cnt2, i);//拷贝原来的值,不过位置可能会发生改变
	}
	if (type == 'R')//更新插入后的行数或者列数
		r = cnt2;
	else
		c = cnt2;
}

int main()
{
	int r1, c1, r2, c2, q, kase = 0;//
	char cmd[10]; //指令数组
	memset(d, 0, sizeof(d));//将数组d归零
	while (scanf("%d%d%d", &r, &c, &n) == 3 && r)//输入单元格的行与列,n代表指令执行的次数,并且上r是因为不能是0行0列;输入0 0退出
	{
		int r0 = r, c0 = c;
		for (int i = 1; i <= r; i++)//对数组赋值注意是从[1][1]开始的以0为行和以0为列不用,这样是方便对应各中的行列序号
		{
			for (int j = 1; j <= c; j++)
			{
				d[i][j] = i * BIG + j;//这里的赋值i * BIG + j是为了记录原始位置
			}
		}
		while (n--)//指令执行
		{

			scanf("%s", cmd);//输入指令
			if (cmd[0] == 'E')//进入交换指令
			{
				scanf("%d%d%d%d", &r1, &c1, &r2, &c2);//输入交换的两个坐标
				//交换
				int t = d[r1][c1];
				d[r1][c1] = d[r2][c2];
				d[r2][c2] = t;
			}
			else//进入删除和插入指令
			{
				int a, x;
				scanf("%d", &a);//a为需要删除或插入的  行的次数或者列的次数
				memset(cols, 0, sizeof(cols));//cols数组就是需要交换的行或者列,置0
				for (int i = 0; i < a; i++)
				{
					scanf("%d", &x);//输入删除或者插入的行或者列
					cols[x] = 1;//置为1,作为标记
				}
				//指令执行
				if (cmd[0] == 'D')//进入删除函数-->“D”
					del(cmd[1]);//指令数组的第二位是区别操作对象行与列的-->“R”/ “C”
				else              //进入插入函数-->“I”
					ins(cmd[1]);//指令数组的第二位是区别操作对象行与列的-->“R”/ “C”
			}
		}
		//以上操作目的是为了得到  数组d, r, c 更新后的状态
		memset(ans, 0, sizeof(ans));
		for (int i = 1; i <= r; i++)
		{
			for (int j = 1; j <= c; j++)
			{
				ans[d[i][j] / BIG][d[i][j] % BIG] = i * BIG + j;//ans[原始对应行坐标][原始对应列坐标] = 现在重排后的的坐标,没有赋值的即为0,不存在了
			}
		}
			if (kase > 0)//第一次kase为0,后面每次输出换行
				printf("\n");
			printf("Spreadsheet #%d\n", ++kase);//多次输出的提示
			scanf("%d", &q);//q代表需要查看坐标的次数
			while (q--)
			{
				scanf("%d%d", &r1, &c1);
				printf("Cell data in (%d,%d)", r1, c1);
				if (ans[r1][c1] == 0)
					printf("GONE\n");//不存在了,因为置为0了
				else
					printf("moved to (%d %d)\n", ans[r1][c1] / BIG, ans[r1][c1] % BIG);
			}
	}

	return 0;
}

思路二:创建结构体,把每组模拟执行的操作信息放在一个结构体中,当查询时再进行每个操作,不需要将整个表的变化进行计算,只需要关注所查询的单元格的位置变化。

#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[i]是操作的行列序号
}cmd[maxd];//变量声明

int r, c, n;

int simulate(int* r0, int* c0)//要查找坐标的指针
{
	for (int i = 0; i < n; i++)//操作次数
	{
		if (cmd[i].c[0] == 'E')//交换
		{
			//如果要查找的数与交换的数无关,就没必要交换了
			if (cmd[i].r1 == *r0 && cmd[i].c1 == *c0)
			{
				*r0 = cmd[i].r2;
				*c0 = cmd[i].c2;
			}
			else if (cmd[i].r2 == *r0 && cmd[i].c2 == *c0)
			{
				*r0 = cmd[i].r1;
				*c0 = cmd[i].c1;
			}
		}
		else//插入或者删除
		{
			int dr = 0, dc = 0;
			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++;
				}
				else//删除
				{
					if (cmd[i].c[1] == 'R' && x == *r0)//删除掉了要查找的数所在的行
						return 0;
					if (cmd[i].c[1] == 'C' && x == *c0)//删除掉了要查找的数所在的列
						return 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()
{
	int r0, c0, q, kase = 0;
	while (scanf("%d%d%d", &r, &c, &n) == 3 && r)//输入行,列数和模拟操作次数//输入0 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 (kase > 0)
			printf("\n");
		printf("Spreadsheet #%d\n", ++kase);

		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;
}

文章来源:https://blog.csdn.net/cookies_s_/article/details/135140686
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。