当前位置:   article > 正文

洛谷P1111 修复公路P8783统计子矩阵

洛谷P1111 修复公路P8783统计子矩阵

P1111 修复公路

题目背景

A 地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车。政府派人修复这些公路。

 题目描述

给出 A 地区的村庄数 $N$,和公路数 $M$,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)。

输入格式

第 1 行两个正整数 N,M。

下面 M 行,每行 3 个正整数 x,y,t,告诉你这条公路连着 x,y 两个村庄,在时间t时能修复完成这条公路。

 输出格式

如果全部公路修复完毕仍然存在两个村庄无法通车,则输出 -1,否则输出最早什么时候任意两个村庄能够通车。

分析

这道题考察的是并查集,俩村是否有修好路就是看这两村是否在同一个集合里,即根结点是否相同

在这里可以先把路按修路时间排序在进行判断两个村子有没有通车即有没有相同结点

  1. class DSU:
  2. def __init__(self, n):
  3. self.parent = list(range(n))
  4. self.rank = [0] * n
  5. def find(self, x):
  6. if self.parent[x] != x:
  7. self.parent[x] = self.find(self.parent[x])
  8. return self.parent[x]
  9. def union(self, x, y):
  10. rootX = self.find(x)
  11. rootY = self.find(y)
  12. if rootX != rootY:
  13. if self.rank[rootX] > self.rank[rootY]:
  14. self.parent[rootY] = rootX
  15. elif self.rank[rootX] < self.rank[rootY]:
  16. self.parent[rootX] = rootY
  17. else:
  18. self.parent[rootY] = rootX
  19. self.rank[rootX] += 1
  20. return True
  21. return False
  22. def earliestAllConnected(N, M, roads):
  23. dsu = DSU(N)
  24. roads.sort(key=lambda x: x[2]) # 按修复时间排序
  25. latestTime = 0
  26. connectedComponents = N
  27. for x, y, t in roads:
  28. if dsu.union(x - 1, y - 1):
  29. latestTime = max(latestTime, t)
  30. connectedComponents -= 1
  31. if connectedComponents == 1: # 所有村庄都连接起来了
  32. return latestTime
  33. if connectedComponents > 1:
  34. return -1 # 仍然存在无法通车的村庄
  35. return latestTime
  36. # 从标准输入读取数据
  37. N, M = map(int, input().split())
  38. roads = [tuple(map(int, input().split())) for _ in range(M)]
  39. # 输出结果
  40. print(earliestAllConnected(N, M, roads))

P8783 统计子矩阵

 题目描述

给定一个 N * M 的矩阵 A,请你统计有多少个子矩阵 (最小 1 *1, 最大 N *M) 满足子矩阵中所有数的和不超过给定的整数 K。

 输入格式

第一行包含三个整数 N, M 和 K。

之后 N 行每行包含 M 个整数, 代表矩阵 A。

输出格式

一个整数代表答案。

分析

这一题首先要把前缀矩阵和求出,这样才能求出子矩阵和

前缀矩阵和:    s[i][j] = a[i][j] + s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1];

  1. 原始矩阵a:
  2. 1 2 3 4
  3. 5 6 7 8
  4. 9 10 11 12
  5. 13 14 15 16
  6. 前缀和矩阵S:
  7. 1 3 6 10
  8. 6 14 24 36
  9. 15 33 54 78
  10. 28 60 96 136

现在,我们想计算从(2,2)(3,3)的子矩阵的和。根据前缀和矩阵,我们可以这样计算:

  • 整个矩形(1,1)到(3,3)的和:S[3][3] = 54
  • 左边矩形(1,1)到(3,1)的和:S[3][1] = 15
  • 上面矩形(1,1)到(1,3)的和:S[1][3] = 6
  • 左上角重叠部分(1,1)的和:S[1][1] = 1

而子矩阵和=大矩阵和-左边矩阵和-上边矩阵+(左边矩阵与上边矩阵重和部分)

所以,从(2,2)(3,3)的子矩阵的和是:54 - 15 - 6 + 1 = 34,这正好是6+7+10+11的和。

  1. #include <stdio.h>
  2. const int MAXN = 507;
  3. int a[MAXN][MAXN];
  4. int s[MAXN][MAXN];
  5. int main() {
  6. int n, m, k;
  7. long long ans = 0; // 用于存储最终的答案
  8. scanf_s("%d%d%d", &n, &m, &k); // 输入n, m, k
  9. // 输入矩阵并计算前缀和
  10. for (int i = 1; i <= n; ++i) {
  11. for (int j = 1; j <= m; ++j) {
  12. scanf_s("%d", &a[i][j]);
  13. // 计算前缀和
  14. s[i][j] = a[i][j] + s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1];
  15. }
  16. }
  17. // 枚举所有可能的子矩阵,并统计满足条件的子矩阵数量
  18. for (int i = 1; i <= n; ++i) {
  19. for (int j = i; j <= n; ++j) {
  20. for (int l = 1, r = 1; r <= m; ++r) {
  21. // 调整左边界l,使得子矩阵的和不超过k
  22. while (l <= r && s[j][r] - s[j][l - 1] - s[i - 1][r] + s[i - 1][l - 1] > k) {
  23. ++l;
  24. }
  25. // 统计数量
  26. ans += (r - l + 1);
  27. }
  28. }
  29. }
  30. // 输出结果
  31. printf("%lld\n", ans);
  32. return 0;
  33. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/374352
推荐阅读
相关标签
  

闽ICP备14008679号