题目:2021 Round-A .

K-Goodness String

签到题,计算当前字符串的 K-Goodness Score ,然后与给出的 K 做差即可。

#include <iostream>
#include <string>
using namespace std;
int cnt = 1;
int solve(const string &s, int n, int k)
{
int cur = 0;
for (int i = 0; i < n / 2; i++) cur += s[i] != (s[n - i - 1]);
return abs(k - cur);
}
int main()
{
int t, n, k;
string str;
cin >> t;
while (t--)
{
cin >> n >> k;
cin.ignore();
cin >> str;
cin.ignore();
printf("Case #%d: %d\n", cnt++, solve(str, n, k));
}
}

L Shaped Plots

L 字要求长边的长度是短边的 2 倍。短边长度至少为 2 ,长边长度至少为 4 。

枚举每个 matrix[i, j] == 1 的点(把该位置视为 L 的拐点)求出其上下左右四个方向的 1 的长度,通过 {up, down, left, right} 4 个变量记录。

然后对于 4 个方向,把该方向作为短边尝试一次,计算以该方向作为短边的 L 的个数。

以下面输入为例子:

1 0 0 0
1 0 0 1
1 1 1 1
1 0 1 0
1 0 1 0
1 1 1 0

在位置 (2, 2) 上,其 4 个方向的 1 的长度为 {up=1, down=4, left=3, right=1} ,那么:

  • up = 1 ,该方向不能为短边。
  • down = 4 ,以该方向为短边时,那么长边必然是 left 或者 right 。 在 down 方向上,短边长度可以为 2, 3, 4, 但 left, right 作为长边均不满「长边长度至少 4 的要求」。
  • left = 3 ,以该方向作为短边,短边长度可以为 2 或者 3,当为 2 时,可以与 down 组成 1 个 L 字。
  • right = 2 ,以该方向作为短边,长度为 2 ,可以与 down 组成 1 个 L 字。

因此在位置 (2, 2) 上共有 2 个 L 字。

时间复杂度 \(O(n^3)\) .

#include <iostream>
#include <vector>
#include <array>
using namespace std;
#define MAXN 1001
int nums[MAXN][MAXN] = {{0}};
int cnt = 1;
int rows, cols;
array<int, 4> getFourLength(int i, int j)
{
int up, down, left, right;
up = down = i, left = right = j;
while (up >= 0 && nums[up][j]) up--;
while (down < rows && nums[down][j]) down++;
while (left >= 0 && nums[i][left]) left--;
while (right < cols && nums[i][right]) right++;
return array<int, 4>({i - up, down - i, j - left, right - j});
}
int solve()
{
int ans = 0;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
if (nums[i][j] == 0) continue;
auto [up, down, left, right] = getFourLength(i, j);
auto calc = [&ans](int shortedge, int longedge1, int longedge2) {
for (int d = 2; d <= shortedge; d++)
ans += (longedge1 >= 2*d), ans += (longedge2 >= 2*d);
};
calc(up, left, right), calc(down, left, right);
calc(left, up, down), calc(right, up, down);
}
}
return ans;
}
int main()
{
int t;
cin >> t;
while (t--)
{
cin >> rows >> cols;
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
cin >> nums[i][j];
printf("Case #%d: %d\n", cnt++, solve());
}
}

Rabbit House

题目的意思就是说,每个「极高点」(盗用了函数中的极值点的概念),其周围 4 个方向的点都必须与它等高,或者比它高度小 1。

如果只有一个最高点,那么是很简单的(一次 BFS 即可完成),但这里可能会出现多个最高点。

这里我们采用堆思想,从堆顶得到最高点,观察其 4 个相邻点的高度,判断是否需要填充高度。若需要,那么更新这个相邻点的高度(同时记录增加了多少高度),并把这个相邻点也放入堆中。

代码实现用了 set 取替代堆了,反正我们只要能在 \(O(1)\) 时间内得到最高点即可。

时间复杂度 \(O(n\log{n}), n=rc\) .

#include <iostream>
#include <vector>
#include <set>
using namespace std;
#define N 301
int t, rows, cols;
int graph[N][N] = {{0}};
struct node_t
{
int x, y, height;
node_t(int xx = -1, int yy = -1, int hh = 0) : x(xx), y(yy), height(hh) {}
bool operator<(const node_t &n) const
{ return height > n.height || (height == n.height && (x < n.x || (x == n.x && y < n.y))); }
};
uint64_t solve(set<node_t> &s)
{
uint64_t ans = 0;
static const vector<pair<int, int>> dirs = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
while (!s.empty())
{
auto [x, y, height] = *s.begin();
s.erase(s.begin());
for (auto &d : dirs)
{
int dx = x + d.first, dy = y + d.second;
if (dx < 0 || dx >= rows || dy < 0 || dy >= cols) continue;
if (graph[dx][dy] <= height - 2)
{
ans += height - 1 - graph[dx][dy];
s.erase(node_t(dx, dy, graph[dx][dy]));
graph[dx][dy] = height - 1;
s.emplace(dx, dy, graph[dx][dy]);
}
}
}
return ans;
}
int main()
{
cin >> t;
for (int k = 1; k <= t; k++)
{
cin >> rows >> cols;
set<node_t> s;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
cin >> graph[i][j];
s.emplace(i, j, graph[i][j]);
}
}
printf("Case #%d: %llu\n", k, solve(s));
}
}

Checksum

一个重要结论:如果某行/某列,只有一个元素被损坏,那么是可以通过该行/该列的校验和进行还原的,这种情况下不需要任何的 cost .

我们把每行,每列都看作一个点,如果位置 (i, j) 为 -1 ,那么就把 row[i]col[j] 连接起来,这样会得到一个二分图。

对于度为 1 的点,说明该行/列,只有 1 个 -1,那么这个数据可以通过检验和还原,不需要任何的 cost ,我们在图中去掉所有这样的边,并且去掉之后,产生的新的度为 1 的点也需要同样的操作。

上述的性质表明,如果得到图是一颗树,那么所有的数据都能通过校验和还原。

比如下图的边 (row[1], col[2])(row[4], col[1]) 就是这样的边。

对于剩下的边所组成的图中,我们要去掉一些边,然后令整个图成为一棵树。显然,去除 (row[i], col[j]) 这条边时,需要耗费 B[i, j]

我们的目的是,去除权值最小的边,得到图的一棵树。换而言之,就是求出图的最大生成树 tree 。那么去除边所使用的权值就是 sum(graph) - sum(tree) .

代码实现

#include <iostream>
#include <unordered_map>
#include <vector>
#include <queue>
using namespace std;
#define N 502
#define getrowid(r) (r)
#define getcolid(c) (n + c)
int nt;
int A[N][N] = {{0}}, B[N][N] = {{0}};
int R[N] = {0}, C[N] = {0}; struct node_t
{
int x, y, cost;
node_t(int xx, int yy, int cc) : x(xx), y(yy), cost(cc) {}
bool operator<(const node_t &node) const { return cost < node.cost; }
}; int find(vector<int> &root, int x) { return root[x] == -1 ? x : root[x] = find(root, root[x]); } uint64_t solve(int n)
{
unordered_map<int, unordered_map<int, int>> g;
vector<int> degree(2 * n + 1, 0);
int rid, cid;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (A[i][j] == -1)
{
rid = getrowid(i), cid = getcolid(j);
g[rid][cid] = g[cid][rid] = B[i][j];
degree[rid]++, degree[cid]++;
}
}
}
// 去除度为 1 的点和这条边
for (int i = 1; i <= 2 * n; i++)
{
if (degree[i] == 1)
{
int vex = g[i].begin()->first;
degree[i]--, degree[vex]--;
g.erase(i);
if (g.count(vex))
{
g[vex].erase(i);
if (g[vex].empty()) g.erase(vex);
}
}
}
// 使用 并查集 + Kruskal 求最大生成树
priority_queue<node_t> q;
vector<int> root(2 * n + 1, -1);
uint64_t sum = 0;
for (auto &[x, m] : g)
for (auto [y, c] : m)
q.emplace(x, y, c), sum += c;
// g 是邻接矩阵,里面有重复的边
sum /= 2;
// cost 计算最大生成树
uint64_t cost = 0;
while (!q.empty())
{
auto [x, y, c] = q.top();
q.pop();
x = find(root, x), y = find(root, y);
if (x != y)
{
root[y] = x;
cost += c;
}
}
return sum - cost;
}
int main()
{
cin >> nt;
int n;
for (int t = 1; t <= nt; t++)
{
cin >> n;
uint64_t sum = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> A[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> B[i][j];
for (int j = 1; j <= n; j++) cin >> R[j];
for (int j = 1; j <= n; j++) cin >> C[j];
printf("Case #%d: %llu\n", t, solve(n));
}
}

最新文章

  1. Win10 下安装 NodeJS
  2. 在.NET中使用反射实现简易插件机制
  3. GCD使用dispatch_semaphore_t创建多线程网络同步请求
  4. [c++基本语法]——构造函数初始化列表
  5. NOIP2006 能量项链
  6. the account is currently locked out. The system administrator can unlock it.
  7. gulp简单使用小记
  8. Python闭包详解
  9. 解决安装WordPress主题及插件需要输入FTP问题
  10. Struts2的validator和WEB-INF下页面交互以及路径问题
  11. Android Studio查看应用数字签名-android学习之旅(76)
  12. linux挂载硬盘分区
  13. LVS+Keepalived搭建高可用负载均衡
  14. Java之相对路径找不到文件问题解决方法
  15. 如何让html中的td文字只显示部分
  16. xml约束的概念
  17. 【linux】——一个小程序
  18. [ATL/WTL]_[初级]_[选择目录对话框]
  19. mysql 时间格式化参数表笔记
  20. Java与JS生成二维码

热门文章

  1. 十大排序算法时间复杂度 All In One
  2. Express All In One
  3. react slot component with args
  4. Puppeteer: 虚拟键盘
  5. 最常用SQL joins:内连接(交集)、左外连接、右外连接、左连接、右连接、全连接(并集),全外连接
  6. MapString转Map
  7. 痞子衡嵌入式:自识别特性(Auto Probe)可以让i.MXRT1060无需FDCB也能从NOR Flash启动
  8. vue 递归调用组件出错
  9. AJAX基本操作
  10. C#无损压缩图片