UVa 512 Spreadsheet Tracking

题目意思就是对电子表格进行一系列操作后追踪各cell的去向,基本操作类型有EX DR DC IR IC 5种,注意一个操作可能包含多个同时进行的子操作

sample input

7 9
5
DR   2  1 5
DC  4  3 6 7 9
IC  1  3
IR  2  2 4
EX 1 2 6 5
4
4 8
5 5
7 8
6 5
0 0

sample output

Spreadsheet #1
Cell data in (4,8) moved to (4,6)
Cell data in (5,5) GONE
Cell data in (7,8) moved to (7,6)
Cell data in (6,5) moved to (1,2)
 
思路一,输出最终表格,在表格中查询原始cell,输出新坐标
我的理解是每次重新操作时的指令都是针对当前表格,但好像与题意有点出入,最后结果不一样,最终程序写得非常繁琐,首先定义一个copy函数,这是5个操作函数的基础,然后考虑主程序,指令判断用了5个基本一样的if分支,由于语句类似,直接复制粘贴。。
下面是源码
#include<iostream>
const int maxn = 50;
int s[maxn][maxn];
int r, c;
void EX(int a, int b, int c, int d);
void copy(char type, int p, int q);
void DR(int n);
void DC(int n);
void IR(int n);
void IC(int n);
int main()
{
	using namespace std;
	cin >> r >> c;
	for (int i = 0; i < maxn; i++)
	{
		for (int j = 0; j < maxn; j++)
			s[i][j] = 0;
	}
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
			s[i][j] = (i + 1) * 10 + j + 1;
	}


	int count;
	cin >> count;
	while (count--)
	{
		char operation[3];
		cin >> operation;
		if (operation[0]=='E')
		{
			int a, b, c, d;
			cin >> a >> b >> c >> d;
			EX(a, b, c, d);
		}
		else if (operation[0] == 'D'&&operation[1] == 'R')
		{
			int m; cin >> m;
			int* op = new int[m];
			for (int i = 0; i < m; i++)
				cin >> op[i];
			for (int i = 0; i<m; i++)
			{
				int k = 0;
				for (int j = 0; j < i; j++)
					if (op[j] < op[i])k++;
				DR(op[i] - k);
			}
			delete[]op;
		}
		else if (operation[0] == 'D'&&operation[1] == 'C')
		{
			int m; cin >> m;
			int* op = new int[m];
			for (int i = 0; i < m; i++)
				cin >> op[i];
			
			for (int i = 0; i<m; i++)
			{
				int k = 0;
				for (int j = 0; j < i; j++)
					if (op[j] < op[i])k++;
				DC(op[i]-k);
			}
			delete[]op;
		}
		else if (operation[0] == 'I'&&operation[1] == 'R')
		{
			int m; cin >> m;
			int* op = new int[m];
			for (int i = 0; i < m; i++)
				cin >> op[i];
		
			for (int i = 0; i<m; i++)
			{	
				int k = 0;
				for (int j = 0; j < i; j++)
					if (op[j] < op[i])k++;
				IR(op[i] + k);
			}
			delete[]op;
		}
		else if (operation[0] == 'I'&&operation[1] == 'C')
		{
			int m; cin >> m;
			int* op = new int[m];
			for (int i = 0; i < m; i++)
				cin >> op[i];
			
			for (int i = 0; i<m; i++)
			{
				int k = 0;
				for (int j = 0; j < i; j++)
					if (op[j] < op[i])k++;
				IC(op[i] + k);
			}
			delete[]op;
		}
		/*for (int i = 0; i < r; i++)
		{
			for (int j = 0; j < c; j++)
				cout << s[i][j] << '\t';
			cout << endl;
		}*/
	}
	cin >> count;
	while (count--)
	{
		int a, b, c;
		cin >> a >> b;
		c = a * 10 + b;
		int ansr = 0, ansc = 0, e = 0;
		for (int i=0; i < r; i++)
		{
			for (int j = 0; j < c; j++)
				if (c == s[i][j])
				{
					ansr = i; ansc = j; e = 1;
				}
		}
		if (e)cout << ansr + 1 << ansc + 1 << endl;
		else cout << "gone" << endl;
	}
}
void EX(int a, int b, int c, int d)
{
	int m = s[a - 1][b - 1];
	s[a - 1][b - 1] = s[c - 1][d - 1];
	s[c - 1][d - 1] = m;
}
void copy(char type, int p, int q)
{
	if (type == 'R')
	{
		for (int i = 0; i < c; i++)
			s[p][i] = s[q][i];
	}
	else
	{
		for (int i = 0; i < r; i++)
			s[i][p] = s[i][q];
	}
}
void DR(int n)
{
	for (int i = n - 1; i < r-1; i++)
		copy('R', i, i + 1);
	for (int i = 0; i < c; i++)
		s[r - 1][i] = 0;
	r--;
}
void DC(int n)
{
	for (int i = n - 1; i < c-1; i++)
		copy('C', i, i + 1);
	for (int i = 0; i < r; i++)
		s[i][c - 1] = 0;
	c--;
}
void IC(int n)
{
	for (int i = r; i >= n; i--)
		copy('R', i, i - 1);
	for (int i = 0; i < c; i++)
		s[n - 1][i] = 0;
	r++;
}
void IR(int n)
{
	for (int i = c; i >= n; i--)
		copy('C', i, i - 1);
	for (int i = 0; i < r; i++)
		s[i][n - 1] = 0;
	c++;
}

debug很痛苦,发现几个平常疏忽的问题
1.char数组的长度要比储存的字符串长度至少加一,最后一位是‘\0';
2.operation是程序中定义的char字符串,但是发现用
if(operation=="EX")

 

进行判断时出现问题,最终只能用单独比较字符的方法代替,可能需要strmp()函数,具体没试不太清楚

3.列表初始化问题,在对数组进行列表初始化为0时发生了一些状况,居然cout无法输出结果,最终用循环代替,还有待查证

4.忽视表达式的副作用,导致在考虑多个操作同时进行时定义了常数k,使用k++计算操作之间的影响,最终让k错误叠加,在使用++,--,cin输入时要小心

5.数组的标号从0开始,有时候出问题

书上的解答也贴在下面

#include<iostream>
#include<cstring>
const int maxd = 100;
const int 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);
void del(char type);
void ins(char type);

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 <= c; 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()
{
	using namespace std;
	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 >> c1 >> r1 >> c2 >> r2;
				int t = d[r1][c1]; d[r1][c1] = d[r2][c2]; d[r2][c2] = t;//??
			}
			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 int(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 > 0)cout << endl;
		cout << "Spreadsheet #" << ++kase;
		cin >> q;
		while (q--)
		{
			cin >> r1 >> c1;
			cout << "Cell data in (" << r1 << "," << c1 << ") ";
			if (ans[r1][c1] == 0)cout << "GONE" << endl;
			else cout << "move to (" << ans[r1][c1] / BIG << "," << ans[r1][c1] % BIG << ")\n";
		}
	}
}

书上的做法中更为简洁,首先是操作函数选择先拷贝再copy,更为直观,关键是在处理同时操作时定义数组cols来标记需要删除或添加的标号,然后用if语句来处理,简化很多,还有将DC DR合并为del函数,进一步简化问题,在判断指令时也更为方便。一个细节就是直接用数组标号对应表格标号,避免了数组从0开始标号的问题。另外,使用了memset,memcpy函数,进一步简化代码。


思路二,不生成表格,直接看每个原始cell的改变,代码如下

#include<iostream>
#include<cstring>
const int maxd = 100;
int simulate(int* r0, int* c0);

struct command {
	char c[5];
	int r1, c1, r2, c2;
	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')
		{
			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()
{
	using namespace std;
	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].r1 >> cmd[i].c1 >> cmd[i].r2 >> cmd[i].c2;
			else
			{
				cin >> cmd[i].a;
				for (int j = 0; j < cmd[i].a; j++)
					cin >> cmd[i].x[j];
			}
        }
		if (kase > 0)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\n";
			else cout << "moved to (" << r0 << "," << c0 << ") ";
		}
	}
}
这样做则更为简单,要对每个变量进行相同操作,就必须把操作储存起来,就要用到数列,但是数列各个元素格式相同,就必然要用到结构。先把所有操作用结构数组储存然后编写simulate程序读取cmd数组的操作并执行。要求自定义函数修改main局部变量,用指针传递即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值