例题4-5 UVA512 Spreadsheet Tracking(53行AC代码)

紫书刷题进行中,题解系列点这里


例题4-5 UVA512 Spreadsheet Tracking

题目大意

给定一个r*c的表格,模拟五种操作,现给出原始单元格坐标,输出最终的单元格坐标,五种操作如下:

  • 删除多行/列
  • 插入多行/列
  • 交换两个单元格

思路分析

有两种思路解决:

  • 整体模拟:直接模拟整个表格的变化,得到最终表格,然后直接查询相应单元格
  • 单个模拟:将命令序列储存起来,对每一个查询的单元格,模拟执行所有命令,输出结果

前者比较直观,符合思维习惯;后者代码量小,在当前数据规模下效率较高

但二者对于操作的模拟逻辑是一致的,假设当前单元格坐标(x,y),当前操作为op,当前操作对应的行列号为i,伪代码如下:

if op == "DR": 
	if x == i: 
		删除(x,y)
    else if x > i: 
		x ++
else if op == "DC":
	if y == i:
		删除(x,y)
    else if y > i:
        y ++
else if op == "IR" && x >= i:
	x ++
else if op == "IC" && y >= i:
    y ++
else if op == "EX":
    if (x,y)属于交换单元格中的一个:
    	x = r
        y = c

算法设计

以下详细阐述整体模拟和单个模拟的算法设计思路。

整体模拟

定义Pos结构体,表示单元格坐标,为了在Map中使用Pos做键值,必须重载<,作为map排序规则

typedef struct Pos{
    int x, y;
    Pos(int _x=0, int _y=0): x(_x), y(_y){} // 默认值表示默认构造函数
    // bool operator < (const Pos& b) const { // map需要排序,必须加const,否则编译报错
    //     if (x == b.x) return y < b.y;
    //     else return x < b.x;
    // }
    friend bool operator < (const Pos& a, const Pos& b) { // 友元函数,map需要排序
        if (a.x == b.x) return a.y < b.y;
        else return a.x < b.x;
    }
} Pos;

定义Map<Pos,Pos>mp1,mp2,mp3,三者表示含义如下:

  • mp1:当前位置->原始位置(始终表示当前表格状态)
  • mp2:操作模拟的中间变量(存储表格中间态)
  • mp3:原始位置->最终位置(逆置mp1的键值对,便于查询)

为了解决插入或删除多行/列时,行列号小者先执行会影响行列号大的问题,有两种思路:

  • 重载<:在结构体中重载运算符<时,定义坐标大者在前,可避免该问题
  • 计数:定义vector<int>cnt(60),cnt[i]表示小于等于i的个数。比如当前命令为插入第五行,那么仅需令x+=cnt[5],若是删除第五行,仅需令x-=cnt[4](删除是严格大于i)

计数实现也简单,分两步:

  • 哈希计数:统计当前命令序列每个数字出现的次数,即若当前数字为i,则cnt[i]++
  • 一遍累加:cnt[i]+=cnt[i-1],i从1开始,cnt[0]=0
单个模拟

定义vector<pair<string,vector<int> > > cmd,存储当前表格的所有操作命令序列,其中string表示五种操作,vector<int>表示操作命令对应的行列集合。

在遍历插入/删除集合前,对其进行降序排列,避免小者对大者产生影响

此处实现思路与思路分析中的伪代码一致,不再赘述。

注意点

  • 输出时两个表格间需要一个空行,最后一个表格无需多余空行
  • 使用结构体做Map的key时,必须重载<,否则无法通过编译,重载可用成员函数/友元函数方式
  • 插入或删除多行/列,注意处理行列号的顺序,大者先执行

AC代码(C++11)

整体模拟(Map)
#include<bits/stdc++.h>
using namespace std;
typedef struct Pos{
    int x, y;
    Pos(int _x=0, int _y=0): x(_x), y(_y){} // 默认值表示默认构造函数
    // bool operator < (const Pos& b) const { // map需要排序,必须加const,否则编译报错
    //     if (x == b.x) return y < b.y;
    //     else return x < b.x;
    // }
    friend bool operator < (const Pos& a, const Pos& b) { // map需要排序
        if (a.x == b.x) return a.y < b.y;
        else return a.x < b.x;
    }
} Pos;
map<Pos,Pos> mp1, mp2, mp3; // 当前位置->原始位置,操作模拟的中间变量,原始位置->最终位置
int r, c, op, n, t, num = 0;
string s;
vector<int> cnt(60);
int main() {
    while(scanf("%d %d", &r, &c) == 2 && (r != 0 && c != 0)) {
        scanf("%d", &op);
        if (num != 0) puts("");
        printf("Spreadsheet #%d\n", ++num);
        mp1.clear(); // 清空
        for (int i = 1; i <= r; i ++) { // 初始化:当前位置->原始位置
            for (int j = 1; j <= c; j ++) {
                mp1.insert({Pos{i,j}, Pos{i,j}});
            }
        }
        while(op --) { // 操作
            cin >>s;
            if (s != "EX") { // 不为Exchange
                cin >>n;
                fill(cnt.begin(), cnt.end(), 0); // 初始化
                set<int> rcset; // 要删除/插入的行列集合
                while(n --) {
                    cin >>t; rcset.insert(t);
                    cnt[t] ++; // 统计每个序号出现次数
                }
                for (int i = 1; i < cnt.size(); i ++) cnt[i] += cnt[i-1]; // cnt[i]表示小于等于i的个数,用于计算最终行列号
                for (auto p = mp1.begin(); p != mp1.end(); p ++) { // 遍历每个cell
                    if (s == "DR") { // 删除行
                        if (rcset.find(p->first.x) == rcset.end()) // 未找到,不被删除
                            mp2.insert({{p->first.x - cnt[p->first.x-1],p->first.y}, p->second}); // 插入记录
                    }
                    else if (s == "DC") { // 删除列
                        if (rcset.find(p->first.y) == rcset.end()) // 不被删除
                            mp2.insert({{p->first.x, p->first.y-cnt[p->first.y-1]}, p->second});
                    }
                    else if (s == "IR") { // 插入行
                        mp2.insert({{p->first.x+cnt[p->first.x], p->first.y}, p->second}); // 更新位置
                    }
                    else if (s == "IC") {
                        mp2.insert({{p->first.x,p->first.y+cnt[p->first.y]}, p->second}); // 更新位置
                    }
                }
                mp1 = mp2; mp2.clear(); // 更新当前位置关系
            }
            else {
                int r1,c1,r2,c2;
                cin >>r1 >>c1 >>r2 >>c2;
                swap(mp1[{r1,c1}], mp1[{r2,c2}]); // 交换单元格,考虑交换单元格包含刚插入的
            }
        }
        mp3.clear();
        for (auto p : mp1) mp3.insert({p.second, p.first}); // 逆置:原始位置->最终位置
        cin >>n;
        int x, y;
        while (n --) { // 处理查询
            cin >>x >>y;
            if (mp3.find({x,y}) == mp3.end()) printf("Cell data in (%d,%d) GONE\n", x, y);
            else printf("Cell data in (%d,%d) moved to (%d,%d)\n", x, y, mp3[{x,y}].x, mp3[{x,y}].y);
        }
    }
    return 0;
}
单个模拟
#include<bits/stdc++.h>
using namespace std;
int r, c, op, cnt = 0;
string s;
int main() {
    while (scanf("%d %d", &r, &c) == 2 && (r != 0 && c != 0)) {
        scanf("%d", &op); // 操作个数
        printf("%sSpreadsheet #%d\n", cnt != 1 ? "\n":"", ++cnt); // ++优先级高,控制空行
        vector<pair<string,vector<int> > > cmd; // 存储命令和对应的行列集合
        while (op --) { // 存储输入命令
            cin >>s;
            cmd.push_back({s, vector<int>()}); // 初始化
            int n, t;
            if (s == "EX") n = 4;
            else scanf("%d", &n);
            while (n --) {
                scanf("%d", &t);
                cmd.back().second.push_back(t);
            }
        }
        int n, x, y, ansX, ansY;
        scanf("%d", &n);
        while (n --) { // n个查询
            scanf("%d %d", &x, &y);
            ansX = x; ansY = y; // 最终结果初始化
            for (auto p : cmd) { // 遍历所有命令
                if (p.first != "EX") { // 插入/删除
                    sort(p.second.begin(), p.second.end(), [](int& a, int &b) {return a > b;}); // 降序排列
                    for (auto i : p.second) { // 遍历删除或插入的行/列号(从大到小,否则前面会影响后面)
                        if (p.first == "DR") { // 删除行
                            if (i == ansX) ansX = -1; // -1标记删除
                            else if (ansX > i) ansX --; // 行号大于i才改变
                        }
                        else if (p.first == "DC") { // 删除列,与删除行类似
                            if (i == ansY) ansY = -1;
                            else if (ansY > i) ansY --;
                        }
                        else if (p.first == "IR" && ansX >= i) ansX ++; // 插入行,>=i就会改变
                        else if (p.first == "IC" && ansY >= i) ansY ++; // 插入列,>=i就会改变
                        if (ansX == -1 || ansY == -1) break; // 发现删除,直接结束
                    }
                }
                else { // 交换单元格
                    if (ansX == p.second[0] && ansY == p.second[1]) ansX = p.second[2], ansY = p.second[3];
                    else if (ansX == p.second[2] && ansY == p.second[3]) ansX = p.second[0], ansY = p.second[1];
                }
            }
            if (ansX == -1 || ansY == -1) printf("Cell data in (%d,%d) GONE\n", x, y);
            else printf("Cell data in (%d,%d) moved to (%d,%d)\n", x, y, ansX, ansY);
        }
    }
    return 0;
}
Vue + x-data-spreadsheet 是一种结合了 Vue.js 框架和 x-data-spreadsheet 库来实现在线Excel表格功能的方法。x-data-spreadsheet 是一个基于 Vue 的表格组件库,可以实现类似 Excel 的表格操作功能。 以下是一个简单的实现示例: 首先,你需要安装 x-data-spreadsheet: ```shell npm install x-data-spreadsheet --save ``` 然后,在你的 Vue 组件中引入并使用 x-data-spreadsheet: ```vue <template> <div> <x-data-spreadsheet v-model="spreadsheet" :config="config" @cell-value-changed="onCellValueChanged" @selection-changed="onSelectionChanged" /> </div> </template> <script> import XDataSpreadsheet from 'x-data-spreadsheet'; import 'x-data-spreadsheet/dist/index.css'; export default { components: { XDataSpreadsheet, }, data() { return { spreadsheet: {}, // 用于绑定数据,可以是数组或对象 config: { width: 800, height: 600, defaultColWidth: 80, defaultRowHeight: 20, // 其他配置... }, }; }, methods: { onCellValueChanged(row, col, value) { // 单元格值变化时的回调函数 }, onSelectionChanged(start, end, cells) { // 选区变化时的回调函数 }, }, }; </script> ``` 在上面的代码中,我们创建了一个 Vue 组件,其中包含了 x-data-spreadsheet 组件的实例。我们使用 `v-model` 绑定了 `spreadsheet` 数据,这个数据将代表表格的内容。`config` 对象用于配置表格的基本属性,例如宽度、高度等。我们还定义了两个方法 `onCellValueChanged` 和 `onSelectionChanged`,用于处理单元格值变化和选区变化的事件。 为了实现在线Excel表格功能,你需要根据实际需求编写业务逻辑代码,比如处理数据的增删改查等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值