其他分享
首页 > 其他分享> > UVA512A(直接进行模拟的版本)

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