1001又是苹果


西电ACM竞赛第1001题


问题描述
最近,亮亮和小W都对苹果很感兴趣!在研究了“最大苹果矩阵”和“给苹果树施肥”的问题后,他们又遇到了一个新的问题:

有一块长n米、宽m米的地,现在小W把地划分成边长1米的小正方形块,共n*m个块。每个块中可能种有一棵苹果树,或放有一个iPhone,也可以什么也没有。然而,亮亮拥有一种超能力,可以将2个宽1米、长度相同的矩形块在空间中直接交换。

亮亮经常对着农场施展超能力,为了不把自己搞晕,他每次总是选择两整行(长度均为m米)或两整列(长度均为n米)进行交换。小W对此十分恼火——当他想拿iPhone打游戏时,却莫名其妙地撞在了苹果树上。小W要求你写一个程序,帮助他确定某一正方形块中放了什么东西。

输入
输入包含多组数据,请处理到文件结束。
每组数据,第一行2个整数n、m,表示地的尺寸。
之后n行,每行m个英文字母,大写的T表示这里种有苹果树,小写的i表示这里放有iPhone,其他字符表示这里什么也没有。
之后1行,一个整数Q,表示小W询问的次数。
之后Q行,包含3个整数,可能有以下情况:
1 i1 j1 表示小W想知道第i1行第j1列的方块中有什么东西。
2 i1 i2 表示亮亮交换了第i1行与第i2行。
3 j1 j2 表示亮亮交换了第j1列与第j2列。
对于100%的数据,有1<=n*m<=106,1<=Q<=105,1<=i1, i2<=n,1<=j1, j2<=m。

输出
对于每组数据,输出以一行“Case #x:”开头,x表示数据的编号,从1开始。
对于小W的每次格式为“1 i j”的询问,输出一行。若方块中是苹果树,输出"Tree"。若方块中是iPhone,输出“Phone”。若方块中什么也没有,输出“Empty”。

样例输入
2 2
Ti
Ti
2
1 1 1
1 2 2
3 3
Tct
Iai
qqT
3
1 1 3
2 1 2
1 1 3

样例输出
Case #1:
Tree
Phone
Case #2:
Empty
Phone


仔细分析问题不难得出解决此问题需要以下几个步骤:

  • 获取场地的长和宽,也就是n(行数),m(列数)
  • 将缓存区内场地每个格子的内容存放到内存中,如果开辟一个max{n,m} * max{n,m}的数组有可能会超过内存最大限制128MB,所以怎么存储这些场地数据直接影响到之后的数据处理
  • 获取指令的条数Q(包括问题和亮亮的操作)
  • 循环Q次依次获取指令,分情况讨论具体的操作
  • 结束一次流程,计数器加一,开始处理下一次数据

解决方案

我在思考这个问题的时候得出了两种处理方法:


第一种是利用三元组来存放整个矩阵数据。方法是先用malloc分配一段大小为(m*1)的空间用于存放一行数据,初始化一个三元组链表。利用循环挨个检查每个数据,如果是字符’i’ 或者 字符’T’,申请一个三元组的节点,将该字符所处在的行列号赋值给节点,字符信息也赋值,接入链表,处理完一行数据后,重新往缓存区里接收一行新的数据,重复上述步骤处理,直到所有矩阵中的’i’和’k’字符信息和行列号全部接入链表。这样做的好处是在之后的行交换和列交换中,只需将待交换的节点的行或列换成要交换的行或列,举个例子:
有三元组节点N1={2,2,‘i’} // {行号,列号,字符信息},指令为“2 1 2”,意思就是要交换第1行和第2行。这时,我们可以很简单的将N1节点的行号2换为1即可,若第一行和第二行还有其他节点,交换行号即可,这样就完成了一次行交换。
很明显。这种方法对稀疏矩阵处理效率很高,但如果矩阵中大多是’i’和‘T’字符,这种稠密矩阵用这种方法很不佳。


第二种方法是直接malloc分配一个(mn)的空间,数组虽放不下,但要是存放mn个字符,128MB内存还是绰绰有余的。然后将所有矩阵数据依次存放到空间中。在交换行列时只需将对应行列的字符交换即可,但问题是如果是稠密矩阵,这个办法姑且不是太浪费,如果是稀疏矩阵那问题可就大了,白白浪费时间做了好多无用的字符交换,所以这种方法不可取。


以上两种方法处理都有局限性,所以我又想出一种巧妙的方法,就是利用索引数组。第一步和第二种方法一样,先malloc分配一个(m*n)的空间,依次将矩阵元素一行一行的存储到堆空间中。然后我们需要构造两个索引数组index_row[]与index_col[],一个放行号,一个放列号,最开始先初始化为顺序的行号和列号(1,2,3…)。但遇到交换行i和行j时,只需将互换index_row[i]和index_row[j]值即可,列也同理,只需互换index_col[i]和index_col[j]值即可。如果要查询某个位置的内容时只需查询第index_row[i]行,第index_col[j]列即可。这样就达到了即使不交换字符,也能够查到交换后的字符内容,省去了大量时间。下面举个例子再说明一下上述方案:
有以下字符内容:

123
1Tct
2Iai
3qqT
index_row123
index_col123

执行指令 2 1 2:
修改index_row

index_row213

执行指令 2 1 3:
修改index_row

index_row312

查询[3,3]字符只需查询第index_row[3]行,第index_col[3]列,即[2,3],其他指令可以按上面自行模拟。


以上分析由于查询某个位置元素的内容相对简单,所以没有具体阐明。
第一种和第二种我都试了一下都超时了,而且第二种实现相对简单,所以就给出第一种方法源码和第三种,第三种内存4016 KB,运行时间101 MS,语言C,代码长度1764 B。

第一种源码

#include <stdio.h>
#include <stdlib.h>

#define Empty  0
#define iPhone 1
#define Tree     2

typedef struct NODE{
    int row;
    int col;
    char flag;
    
    struct NODE *next;
}Node;

int main(){
    
    int col,row,i,j,num;
    int count = 0;
    char *buf;
    Node *head = NULL;
    Node *node = NULL;
    Node *p;
    
    while(scanf("%d %d",&row,&col) != EOF){
        
        count++;
        
        buf = (char*)malloc(sizeof(char) * col);
        for(i = 0; i < row; ++i){
            scanf("%s",buf);
            for(j = 0; j < col; ++j){
                if(buf[j] == 'i' || buf[j] == 'T'){
                    node = (Node*)malloc(sizeof(Node));
                    node->row = i + 1;
                    node->col = j + 1;
                    node->flag = (buf[j] == 'i') ? iPhone : Tree;
                    node->next = head;
                    head = node;
                }
            }
        }

        scanf("%d",&num);
        if(num >= 1)
            printf("Case #%d:\n",count);
        for(i = 0; i < num; ++i){
            int arg1,arg2,arg3;
            scanf("%d %d %d",&arg1,&arg2,&arg3);
            if(arg1 == 1){
                p = head;
                while(p != NULL){
                    if(p->row == arg2 && p->col == arg3){
                        printf("%s\n",(p->flag == iPhone)? "Phone" : "Tree" );
                        break;
                    }
                    p = p->next;
                }
                if(p == NULL)
                    printf("Empty\n");
            }
            else if(arg1 == 2){
                p = head;
                while(p != NULL){
                    if(p->row == arg2){
                        p->row = arg3;
                    }
                    else if(p->row == arg3){
                        p->row = arg2;
                    }
                    p = p->next;
                }
            }
            else if(arg1 == 3){
                p = head;
                while(p != NULL){
                    if(p->col == arg2){
                        p->col = arg3;
                    }
                    else if(p->col == arg3){
                        p->col = arg2;
                    }
                    p = p->next;
                }
            }
        }

        free(buf);
        while(head != NULL){
            p = head;
            head = head->next;
            free(p);
        }
    }
    
    return 0;
    
}

第三种源码

#include <stdio.h>
#include <stdlib.h>

#define iPhone 'i'
#define Tree     'T'

int main(){
    
    int col,row,i,j,num;
    int count = 0;
    char *buf;
    int *index_col;
    int *index_row;
    
    while(scanf("%d %d",&row,&col) != EOF){
        
        count++;
        
        buf = (char*)malloc(sizeof(char) * col * row);
        index_row = (int*)malloc(sizeof(int) * row);
        index_col = (int*)malloc(sizeof(int) * col);
        
        for(i = 0; i < col; ++i)
            index_col[i] = i;
        for(i = 0; i < row; ++i){
            scanf("%s",buf+col*i);
            index_row[i] = i;
        }
        
        scanf("%d",&num);
        if(num >= 1)
            printf("Case #%d:\n",count);
        for(i = 0; i < num; ++i){
            int arg1,arg2,arg3;
            scanf("%d %d %d",&arg1,&arg2,&arg3);
            if(arg1 == 1){
                    int offset = index_row[arg2-1] * col + index_col[arg3-1];
                    if(*(buf+offset) == iPhone || *(buf+offset) == Tree)
                        printf("%s\n",( *(buf+offset) == iPhone)? "Phone" : "Tree" );
                    else
                        printf("Empty\n");
            }
            else if(arg1 == 2){
                int temp;
                temp = index_row[arg2-1];
                index_row[arg2-1] = index_row[arg3-1];
                index_row[arg3-1] = temp;
            }
            else if(arg1 == 3){
                int temp;
                temp = index_col[arg2-1];
                index_col[arg2-1] = index_col[arg3-1];
                index_col[arg3-1] = temp;
            }
        }
        
        free(buf);
    }
    
    return 0;
    
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值