AtCoder ABC298 F - Rook Score
题目描述
有一个
找到一个点
样例输入输出
4
1 1 2
1 2 9
2 1 8
3 2 3
20
数据范围
前置 - STL
pair<int, int> : 将两个变量关联在一起,组成一个对,这两个变量的类型都是 int。由于 pair<int, int> 过长,我们通常用加上一条语句typedef pair<int, int> PII; 用 PII 代替掉上面的 pair<int, int>。
map<PII, int> : 有序键值对容器,它的元素的键是唯一的。前面的 PII 是我们规定的键类型,后面的 int 是我们规定的值类型。
unordered_map<int, int> : 与 map 类似,无序键值对容器,它的元素的键是唯一的。前面的 int 是我们规定的键类型,后面的 int 是我们规定的值类型。
vector<PII> : 内存连续的、可变长度数组,数组中的元素类型为 int。
分析
介绍完本题需要用到的 STL 容器后,我们开始分析这道题目。
本题的值域巨大,但元素数量不多,因此我们要想一种方法把数据存储起来,
完成后,就要考虑贪心策略,寻找最优解。
以下分为两个部分叙述。
存储数据
看到这个数据范围,我们可以将数据离散化。但那样略显复杂,这里使用 STL 容器进行处理数据。
unordered_map 的内部原理是使用哈希表存储数据,因此它可以存储几乎所有数据类型(几乎的原因下面有提到)。题目中要求每一行或每一列的数字和,所以可以用 unordered_map 存储每一行和每一列的权值和。
我们还需要存储下每一个点的权值,但是如果开二维数组肯定是不可以的,因此考虑使用容器 map。
为什么不能使用 unordered_map 呢?因为我们相当于是有 unordered_map 不允许使用 pair,所以也就不能存储 map 是可以的。
以上的存储都可以在输入时完成:
unordered_map<int, int> R, C;
vector<PII> r, c;
map<PII, int> mp;
cin >> n;
for (int i = 1; i <= n; i ++ )
{
cin >> x >> y >> z;
mp[{x, y}] = z;
R[x] += z;
C[y] += z;
}
由于要找最大值,我们会有一个意识就是需要把数据排序。但 unordered_map 是无法支持排序的,因此这里就用到了 vector。
我们可以把 unordered_map 中的所有数据再次存入 vector 中。由于 unordered_map 中的每一个值都有 Key 和 T 属性,也就是 first 和 second,所以 vector 的数据类型为 pair<int, int>,其中第一个值存储的是当前行的权值和,第二个值存储的是这一行的行数。
将元素搬到 vector 中后,就可以对其进行排序了。pair<int, int> 类型的 sort 排序是按照第一个值从小到大排序,所以排完序后还需要把数组翻转过来。
for (auto e : R) r.push_back({e.second, e.first});
for (auto e : C) c.push_back({e.second, e.first});
sort(r.begin(), r.end());
sort(c.begin(), c.end());
reverse(r.begin(), r.end());
reverse(c.begin(), c.end());
贪心寻找最优解
上面的排序都是为了这一步的贪心做准备。
如果只找最大行和最大列,并把它们相加的话,是肯定不行的。例如下面这个例子。
最大行是第
由于求行和列的和的时候,需要减掉重复部分的数值,所以这个减掉的数值肯定是越小越好。
最小是多少呢?明显为
贪心证明:
如果当前枚举的行为
for (auto _r : r)
for (auto _c : c)
if (mp.find({_r.second, _c.second}) == mp.end())
{
ans = max(ans, _r.first + _c.first);
break;
}
else ans = max(ans, _r.first + _c.first - mp[{_r.second, _c.second}]);
完整代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> PII;
int n, x, y, z, ans;
unordered_map<int, int> R, C;
vector<PII> r, c;
map<PII, int> mp;
signed main()
{
// 读入
cin >> n;
for (int i = 1; i <= n; i ++ )
{
cin >> x >> y >> z;
mp[{x, y}] = z; // map 存储 (x, y) 上的值
R[x] += z; // R 存储当前行上的数字和
C[y] += z; // C 存储当前列上的数字和
}
// 将 unordered_map 中的元素搬到 vector 中,便于排序
for (auto e : R) r.push_back({e.second, e.first});
for (auto e : C) c.push_back({e.second, e.first});
// 从大到小排序
sort(r.begin(), r.end());
sort(c.begin(), c.end());
reverse(r.begin(), r.end());
reverse(c.begin(), c.end());
// 贪心
for (auto _r : r) // 枚举行
for (auto _c : c) // 枚举列
if (mp.find({_r.second, _c.second}) == mp.end()) // 如果 (_r, _c) 的值为 0,更新答案后直接跳出循环
{
ans = max(ans, _r.first + _c.first);
break;
}
else ans = max(ans, _r.first + _c.first - mp[{_r.second, _c.second}]); // 否则更新答案
cout << ans; // 输出答案
return 0;
}