UVa512 Spreadsheet Tracking

题目传送门:https://vjudge.net/problem/UVA-512

题目本身不难,在这儿将两种思路都贴出来,相互学习。

第一种,无脑模拟

#include
#include
#define maxd 100
#define BIG 10000
int r, c, n, d[maxd][maxd], d2[maxd][maxd], ans[maxd][maxd], cols[maxd];/*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];//d2作为d的拷贝,把修改后的表格又重新传回了d
    }
    else//列操作
    {
        for(int i = 1; i <= r; i++)
            d[i][p] = d2[i][q];
    }
}

void del(char type)
{
    memcpy(d2, d, sizeof(d));//mencpy()的作用相当于给d做了一个副本,d2就是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);//再最前面也就是0行(列)插入一行(列)
        //当前行(列)被覆盖为一个空行(列)
        copy(type, ++cnt2, i);//被标记的行标或者列标,就在对应的前面增加一行或者一列
    //在标记行(列)的下方还原被标记覆盖的那一行(列),不用担心下一行被覆盖而消失,因为d2从未被改变过
    //它里面保存着最原始的表格
    }
    if(type == 'R') r = cnt2;
    else c = cnt2;
}

int main()
{
    int r1, c1, r2, c2, q, kase = 0;
    char cmd[10];//command 要执行的操作存储在这个字符数组里
    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--)
        {
            scanf("%s", cmd);
            if(cmd[0] == 'E')//Exchange
            {
                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;//将待操作列(行)标记为1
                }
                if(cmd[0] == 'D') del(cmd[1]);
            //传入指令首字母为D,必为删除指令,直接调用del函数,在里面判断第二个字母是还是C
                else ins(cmd[1]);//既不是EX,又不是Delete,那一定就是插入了,直接调用Ins插入函数
            }
        }
        memset(ans,0,sizeof(ans));
        for(int i = 1; i <= r; i++)
            for(int j = 1; j <= c; j++)    //现在的r和c都是更新后的
                ans[d[i][j]/BIG][d[i][j] % BIG] = i * BIG + j;  // /BIG得出行标,%BIG得出列标
//d中保存的一直都是自己在原表中的位置
//进行完上述循环之后,ans的label就是待查询cell,ans的值就是待查询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;
}

第二种,保存操作,查询cell时再进行操作

#include
#include
#define maxd 10000

//不模拟表格更改的操作,现将操作保存起来,到查询的时候再进行操作,只需要专注于待查询cell即可
struct Command {
    char c[5];//保存操作
    int r1, c1, r2, c2;//记录行数和列数, 这4个成员,只有在Exchange操作中有用
    int a, x[20];//待修改r/col个数及r/col的值
} cmd[maxd];//创建指令结构体数组
int r, c, n;

//simulate的参数只有被查询cell的label
int simulate(int* r0, int* c0) {//由于需要更改r0和c0的值,故而传的是地址
    for(int i = 0; i < n; i++) {

    //要把所有操作全部执行完,才能得出最终结果

        if(cmd[i].c[0] == 'E') { //交换操作
        //便利找出待查询cell在cmd数组中的位置
            if(cmd[i].r1 == *r0 && cmd[i].c1 == *c0) {//同1输2
                *r0 = cmd[i].r2; *c0 = cmd[i].c2;
            }
            else if(cmd[i].r2 == *r0 && cmd[i].c2 == *c0) {//同2输1
                *r0 = cmd[i].r1; *c0 = cmd[i].c1;   
            }
        }
        else {
            int dr = 0, dc = 0; 
            for(int j = 0; j < cmd[i].a; j++) {//对所有待操作r/col进行相应操作
                int x = cmd[i].x[j];//依次取出待操作row/col值
                if(cmd[i].c[0] == 'I') {//insert两个分支,ins_row/ins_col
                    if(cmd[i].c[1] == 'R' && x <= *r0) dr++;
                    //在cell的r_label及r_label前插入,cell的r_label值都会相应的增加,c_label同理
                    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;
                    //待查询cell的r_label值或c_label值被删除,都会立马返回0,simulate程序终止,输出“GONE”
                    if(cmd[i].c[1] == 'R' && x < *r0) dr--;
                    //在cell的r_label前删除,cell的r_label值都会相应的减少,c_label同理
                    if(cmd[i].c[1] == 'C' && x < *c0) dc--;                 
                }
            }
            *r0 += dr; *c0 += dc;
        }   
    }       
    return 1;
}
int main() {
    int r0, c0, q, kase = 0;//输出序号kase
    while(scanf("%d%d%d", &r, &c, &n) == 3 && r) {
        for(int i = 0; i < n; i++) {
            scanf("%s", cmd[i].c);//传入指令
            if(cmd[i].c[0] == 'E') //Exchange需要4位参数,两个cell,在查询时swap即可
                scanf("%d%d%d%d", &cmd[i].r1, &cmd[i].c1, &cmd[i].r2, &cmd[i].c2);
            else {
                scanf("%d", &cmd[i].a);//否则即为删或插操作,待操作row(col)个数
                for(int j = 0; j < cmd[i].a; j++)
                    scanf("%d", &cmd[i].x[j]);//输入label
            }
        }
        if(kase > 0)
            printf("\n");
        printf("Spreadsheet #%d\n", ++kase);

        scanf("%d", &q);//待查询cell个数
        while(q--) {
            scanf("%d%d", &r0, &c0);//待查询cell的label
            printf("Cell data in (%d,%d) ",r0, c0);
            if(!simulate(&r0, &c0)) printf("GONE\n");//关键就在于simulate的实现(simulate模拟)
            else printf("moved to (%d,%d)\n", r0, c0);
        }
    }
    return 0;
}

你可能感兴趣的:(算法竞赛入门经典第二版,c语言,算法)