UVA512A(直接进行模拟的版本)
作者:互联网
这道题目有两种解法,第一种是直接在表格上模拟,另一种是将操作保存起来,然后对查询的cell进行模拟操作
先来说第一种,第一种解法是很多人很自然的想到的一种解法,但是刘汝佳大神的代码中有很多的处理方法值得学习。
下面贴上刘汝佳大神写的代码,之后我会模仿刘汝佳大神的思路再写一遍
// UVa512 Spreadsheet Tracking (算法1) // Rujia Liu #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]; //数据结构r c是一开始的行数和列数 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));//d2中的内容和d中的内容是完全一样的 int cnt = type == 'R' ? r : c, cnt2 = 0; for(int i = 1; i <= cnt; i++) {//如果是要删除行的话,cnt就是行 if(!cols[i]) copy(type, ++cnt2, i);//如果这一row或者col没有被删除,那么就被复制到d2表格中去,copy函数中的i(q)是可以留下来的行,cnt2就是剩下了多少行 } //这一步就相当于依照和d一模一样的d2来重新写d,然后重新更新r 和c 的值,这样就不用将d剩下的部分清0了 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);//需要在第i行插入一行,这个想法特别的巧妙 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(scanf("%d%d%d", &r, &c, &n) == 3 && 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--) {//一共有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)); for(int i = 0; i < a; i++) { scanf("%d", &x); cols[x] = 1; }//col里面保存的是一些需要操作的行或者列 if(cmd[0] == 'D') del(cmd[1]); else ins(cmd[1]);//cmd[1]保存的是行或者列 } } memset(ans, 0, sizeof(ans));//这一步开始写答案 for(int i = 1; i <= r; i++)//依据此时的表格中的内容来得到此时的d[i][j]中存放的是原来的哪一个cell,并记录进ans表格中 for(int j = 1; j <= c; j++) { ans[d[i][j]/BIG][d[i][j]%BIG] = i*BIG+j;//ans中保存的是原来的某个cell,现在在哪个cell中 } if(kase > 0) printf("\n"); printf("Spreadsheet #%d\n", ++kase); scanf("%d", &q); while(q--) { scanf("%d%d", &r1, &c1); printf("Cell data in (%d,%d) ", r1, c1); if(ans[r1][c1] == 0) printf("GONE\n"); else printf("moved to (%d,%d)\n", ans[r1][c1]/BIG, ans[r1][c1]%BIG); } } return 0; }
在汝佳大神的处理方法中,第一点值得学习的地方就是如何处理插入/删除操作。
因为这道题目的插入和删除的定义很奇怪,可以看作是对原来的表格进行一次性的操作,而不是一次一次的操作。
比如说,如果要删除某几行 DR 4 2 6 5. 这个“command”的意思是直接删除原来的表格中的第2 6 5行,这样就带来了问题,如果直接按照顺序删除第2,6,5行的话,需要从上到下进行遍历,那么在删除完第2,6行之后,如何处理删除第5行这种情况?同样对于插入操作也存在类似的问题。
我之前的想法是,给需要进行操作的行数进行排序,从最大的开始处理,即:从下往上开始一直处理,这样就可以保证无论是删除还是插入都可以满足要求,不过插入操作需要将这个数组往下移动。
刘汝佳大神的想法非常的巧妙,他直接使用了一个col数组,来保存需要操作的行或者列,然后从col[1]开始遍历,看哪一行需要操作,至于如何更新表格,汝佳大神的解法就更巧妙了,他直接用另一个与d表格一模一样的d2表格来重写d表格,从而节省了很多的时间。不得不说大神就是大神啊。如果是删除操作,那么这一行就不要再写,如果是插入操作,那么就先插入一行空白的,然后再插入原来的哪一行。
第二点值得学习的地方是如何寻找原来在表格中的cell,现在被移动到哪个地方了。
我原来的想法是每进行一次操作就在ans[][]数组中保存一下答案,但是这样非常的麻烦。
刘汝佳大神的思路是:遍历最后的表格d,d中保存的是原来已经存在的cell的信息。然后将d表格中保存原来的cell信息的cell的信息保存到ans表格中。
下面我将自己重新按照汝佳大神的思路写一遍。
//模仿汝佳大神的思路来写一遍 #include<cstdio> #include<string> #include<algorithm> using namespace std; const int maxn = 50 + 5; int table1[maxn][maxn], table2[maxn][maxn], r_c[maxn], ans[maxn][maxn]; int row_now ,col_now, num_op, row_original, col_original, kase=1; char comd[3]; void print_r_c() { int n=max(row_now,col_now); for(int i=1;i<=n;i++) printf("%3d",r_c[i]); printf("\n\n"); } void print_table() { for(int i=1;i<=row_now;i++) { for(int j=1;j<=col_now;j++) { printf("%5d ",table1[i][j]); } printf("\n"); } printf("\n"); } bool initial() { if(scanf("%d%d%d",&row_now,&col_now,&num_op) == 3 && row_now) { row_original=row_now; col_original=col_now; for(int i = 1;i <= row_now;i++) { for(int j = 1;j <= col_now;j++) { table1[i][j] = i * 100 + j; } } //print_r_c(); //print_table(); } else return false; return true; } void op_d() { if(comd[1]=='R') { memcpy(table2,table1,sizeof(table1));//mem类型的函数操作非常的快 int cnt=1; for(int i = 1;i <= row_now;i++) { if(r_c[i]!=1)//说明此行不需要被删除 { for(int j=1;j<=col_now;j++) { table1[cnt][j]=table2[i][j]; } cnt++; } } row_now=cnt-1; } if(comd[1] == 'C') { memcpy(table2,table1,sizeof(table1));//mem类型的函数操作非常的快 int cnt = 1; for(int i = 1;i <= col_now;i++) { if(r_c[i] != 1)//说明此列不需要被删除 { for(int j = 1;j <= row_now;j++) { table1[j][cnt] = table2[j][i]; } cnt++; } } col_now = cnt-1; } } void op_i() { if(comd[1]=='R') { memcpy(table2,table1,sizeof(table1));//mem类型的函数操作非常的快 int cnt=1; for(int i = 1;i <= row_now;i++) { if(r_c[i]==1)//说明此行需要插入 { for(int j=1;j<=col_now;j++) { table1[cnt][j]=0; } cnt++; } for(int j=1;j<=col_now;j++) { table1[cnt][j]=table2[i][j]; } cnt++; } row_now=cnt-1; } if(comd[1]=='C') { memcpy(table2,table1,sizeof(table1));//mem类型的函数操作非常的快 int cnt = 1; for(int i = 1;i <= col_now;i++) { if(r_c[i]==1)//说明此列需要插入 { for(int j=1;j<=row_now;j++) { table1[j][cnt]=0; } cnt++; } for(int j=1;j<=row_now;j++) { table1[j][cnt]=table2[j][i]; } cnt++; } col_now=cnt-1; } } void operation() { for(int i = 0;i < num_op;i++)//一共有num_op个操作 { scanf("%s",comd); if(comd[0] == 'E') { int r0,r1,c0,c1; scanf("%d%d%d%d",&r0,&c0,&r1,&c1); int temp = table1[r0][c0]; table1[r0][c0] = table1[r1][c1]; table1[r1][c1] = temp; //print_r_c(); //print_table(); } else { memset(r_c,0,sizeof(r_c)); int n; scanf("%d",&n);//一共要操作n行/列 for(int i=0;i<n;i++) { int x; scanf("%d",&x); r_c[x]=1;//此行需要操作 } if(comd[0]=='D') { op_d(); //print_r_c(); //print_table(); } else if(comd[0]=='I') { op_i(); //print_r_c(); //print_table(); } } } } void print_ans() { for(int i=1;i<=row_original;i++) { for(int j=1;j<=col_original;j++) { printf("%5d",ans[i][j]); } printf("\n"); } printf("\n\n"); } void query() { memset(ans,0,sizeof(ans)); for(int i = 1;i <= row_now;i++) { for(int j = 1;j <= col_now;j++) { if(table1[i][j]) { ans[table1[i][j] / 100][table1[i][j] % 100] = i * 100 + j; } } } //print_ans(); int num; scanf("%d",&num); for(int i=0;i<num;i++) { int row,col; scanf("%d%d",&row,&col); if(ans[row][col]) { printf("Cell data in (%d,%d) moved to (%d,%d)\n",row,col,ans[row][col]/100,ans[row][col]%100); } else printf("Cell data in (%d,%d) GONE\n",row,col); } } int main() { #ifdef local freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); #endif while(initial()) { if(kase!=1) { printf("\n"); } printf("Spreadsheet #%d\n",kase++); operation(); query(); } return 0; }
这份代码是我自己写的,虽然看过一次并且已经理解了汝佳大神打代码,但是还有有一些地方并没有完全学会。即:对代码的抽象程度不够高。
主要体现在:在删除和插入操作中,有一部分代码是用于将table2的内容复制到table1当中。这一部分可以单独写成一个函数,就像汝佳大神代码里面的一样(copy函数)。
标签:表格,int,大神,UVA512A,maxn,maxd,版本,ans,模拟 来源: https://www.cnblogs.com/TorettoRui/p/10397459.html