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