UVA - 512 Spreadsheet Tracking

//法1:先模拟操作,算出最后的电子表格,接着在每次查询时直接在电子表格中,找到所求的单元格
/*
  此方法最关键的地方,应是怎样建立原来的位置和后来的位置的联系
  
  入门经典的法一,很巧妙地借助了这样的数组定义;
  d[i][j] = i*BIG + j,记录下了原来的位置...在后面的插入、删除和交换等等步骤之后
  虽然d[i][j]里装的数字可能变化了,可是只要计算 d[i][j] / BIG, d[i][j] % BIG,得到的就是这个位置的初始位置的行数和列数了
  
  以及,填充ans数组时,相当于遍历每个位置,每个位置(i,j)会有一个对应的数字 i * BIG +j,我们可以找到它原来的行列位置,作为ans[i][j]中的i,j(怎么找呢,当然是用d数组里的值整除BIG和取模BIG啦~),因此有 ans[d[i][j] / BIG][d[i][j] % BIG] = i * BIG + j;
  
  它的含义是:原来第i行第j列的数字,现在放到了新的位置,而我如果想知道这个新的位置,只要将 (i * BIG + j) 整除BIG和取模BIG即可
  
  所以,如果填充ans数组时,遇到了原来存在但现在被删掉了的数字,那么原来的行数和列数,一定不会作为ans[i][j]中的i和j,因为既然删除,最初的那个d[i][j]肯定也就被删掉了
  
  如果填充ans数组时,遇到了新加上的数,那么它之前肯定没被填充到d数组,而全局数组d,没被填充的部分就是0,所以,它根本不会影响ans数组的正常填充
  
  总之,这题很巧妙地借助了取模和整除,建立了原来的位置和现在的位置之间的关系,以及表示位置的 i*BIG + j,入门经典中指定的BIG是10000,但其实100就够了,它的目的只是让i被分离出来时,一定不会受到j进位的影响,既然i,j都是最大50,也就是最多2位,BIG用100也就够了
  
  BTW,这题和 HDU - 6098 Inversion 其实有些异曲同工之妙,虽然看上去很不同,但它们都有一个共同点,都要和最初的标号(或是位置)建立上联系,对这题的这种方法,是利用乘法+加法、除法+取模,来将原来的位置和现在的位置建立联系(它们之间有个不会变化的纽带,就是构造出的那个数字,d[i][j] / BIG, d[i][j] % BIG,于是这就保证了,对于某个位置而言,只要它没被删除,无论它移动到那里,在别的位置上,仍然可以通过构造出的那个数字,整除和取模得到原先的位置)
  
  这种思路实在是精妙,让人想对刘汝佳前辈伏地膜...
  
  而hdu那题,是通过构造结构体,将序号和数值绑定起来
  (没记错的话,这道题我已经在我的blog上发了题解啦,需要的不妨看看)
  
  两题的共性就是:都要用一些特定方法或思路,来保证一个不变量,作为最初和最终状态的纽带
  
*/


#include <iostream>
#include <cstring>
using namespace std;
const int maxd = 100;
const int BIG = 100;
int r, c, n, d[maxd][maxd], d2[maxd][maxd], ans[maxd][maxd], cols[maxd];
/*依次表示:行、列、操作次数
d:将d中的值按照最初位置构造 i * BIG + j并赋值,d中的值会改变位置,但通过值仍能推出初始位置(除非删除)
d2:起临时变量作用的数组,方便进行系列操作
ans:下标对应初始位置,值对应最终位置的构造值 i * BIG + j,整除取模可得最终位置
cols:表示每次插入或删除操作波及的行数或列数,因而每次操作前需清空
*/

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)
{
	memcpy(d2, d, sizeof(d));
	int cnt = type == 'R' ? r : c, cnt2 = 0;
	for (int i = 1; i <= cnt; i++)
	{
		if (!cols[i]) copy(type, ++cnt2, i);
	}
	if (type == 'R') r = cnt2; else c = cnt2;
}

void ins(char type)
{
	memcpy(d2, d, sizeof(d));
	int cnt = type == 'R' ? r : c, cnt2 = 0;
	for (int i = 1; i <= cnt; i++)
	{
		if (cols[i]) copy(type, ++cnt2, 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));
	while (cin >> r >> c >> n && r)
	{
		int r0 = r, c0 = c;
		for (int i = 1; i <= r; i++)
		for (int j = 1; j <= c; j++)
		d[i][j] = i * BIG + j;
		
		while (n--)
		{
			cin >> cmd;
			if (cmd[0] == 'E')
			{
				cin >> r1 >> c1 >> r2 >> c2;
				swap(d[r1][c1], d[r2][c2]);
			}
			else
			{
				int a, x;
				cin >> a;
				memset(cols, 0, sizeof(cols));
				for (int i = 0; i < a; i++)
				{
					cin >> x;
					cols[x] = 1;
				}
				if (cmd[0] == 'D') del(cmd[1]);
				else ins(cmd[1]);
			}
		}
		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;
		}
		if (kase) cout << endl;
		cout << "Spreadsheet #" << ++kase << endl;
		cin >> q;
		while (q--)
		{
			cin >> r1 >> c1;
			cout << "Cell data in (" << r1 << "," << c1 << ") ";
			if (ans[r1][c1] == 0) cout << "GONE" << endl;
			else cout << "moved to (" << ans[r1][c1] / BIG << "," << ans[r1][c1] % BIG << ")" << endl;
		}
	}
	return 0;
}

//法二:先将所有的操作保存,然后对于每个查询重新执行每个操作,但不需要计算整个电子表格的变化,而只关注所查询的单元格的位置变化。对于题目给定的规模来说,这个方法不仅更好写,而且效率更高
//入门经典的法二,代码就比较容易理解了,逻辑十分清楚

#include <iostream>
#include <cstring>
using namespace std;
typedef pair<int, int> p;
const int maxd = 10000;

struct Command
{
	char c[5];
	p pos1, pos2; //position 1, position 2
	int a, x[20];
}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')
		{
			p pos3(r0, c0);
			if (cmd[i].pos1 == pos3)
			{
				r0 = cmd[i].pos2.first;
				c0 = cmd[i].pos2.second;
			}
			else if (cmd[i].pos2 == pos3)
			{
				r0 = cmd[i].pos1.first;
				c0 = cmd[i].pos1.second;
			}
		}
		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++;
					else if (cmd[i].c[1] == 'C' && x <= c0) dc++;
				}
				else
				{
					if (cmd[i].c[1] == 'R') 
					{
						if (x == r0) return 0;
						if (x < r0) dr--;
					}
					else
					{
						if (x == c0) return 0;
						if (x < c0) dc--;
					}
				}
			}
			r0 += dr; c0 += dc;
		}
	}
	return 1;
}
int main()
{
	int r0, c0, q, kase = 0;
	while (cin >> r >> c >> n && r)
	{
		for (int i = 0; i < n; i++)
		{
			cin >> cmd[i].c;
			if (cmd[i].c[0] == 'E') cin >> cmd[i].pos1.first >> cmd[i].pos1.second >>  cmd[i].pos2.first >> cmd[i].pos2.second;
			else
			{
				cin >> cmd[i].a;
				for (int j = 0; j < cmd[i].a; j++)
				cin >> cmd[i].x[j];
			}
		}
		if (kase) cout << endl;
		cout << "Spreadsheet #" << ++kase << endl;
		
		cin >> q;
		while (q--)
		{
			cin >> r0 >> c0;
			cout << "Cell data in (" << r0 << "," << c0 << ") ";
			if (!simulate(r0, c0)) cout << "GONE" << endl;
			else cout << "moved to (" << r0 << "," << c0 << ")" << endl;
		}
	}
	return 0;
}


转载于:https://www.cnblogs.com/mofushaohua/p/7789533.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值