鱼C论坛

 找回密码
 立即注册
查看: 2698|回复: 24

不理解悬线算法

[复制链接]
回帖奖励 1 鱼币 回复本帖可获得 1 鱼币奖励! 每人限 1 次(中奖概率 10%)
发表于 2023-8-9 14:58:20 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 Ewan-Ahiouy 于 2023-8-10 21:11 编辑

rt

这有个案例:luogu P4147

my code

  1. #include <bits/stdc++.h>
  2. using namespace std;

  3. const int MAXN = 1010;
  4. int n, m, ans, h[MAXN][MAXN], l[MAXN][MAXN], r[MAXN][MAXN];
  5. char a[MAXN][MAXN];

  6. int main() {
  7.     ans = 0;
  8.     cin >> n >> m;
  9.     for (int i = 1; i <= n; i++) {
  10.         for (int j = 1; j <= m; j++) {
  11.             cin >> a[i][j];
  12.             h[i][j] = 1, r[i][j] = l[i][j] = j;
  13.         }
  14.         for (int j = 2; j <= m; j++) if (a[i][j] == 'F' && a[i][j - 1] == 'F') l[i][j] = l[i][j - 1];
  15.         for (int j = m - 1; j > 0; j--) if (a[i][j] == 'F' && a[i][j + 1] == 'F') r[i][j] = r[i][j + 1];
  16.     }
  17.     for (int i = 1; i <= n; i++)
  18.         for (int j = 1; j <= m; j++) {
  19.             if (i > 1 && a[i][j] == 'F') {
  20.                 if (a[i - 1][j] == 'F') {
  21.                     h[i][j] = h[i - 1][j] + 1;
  22.                     if (l[i - 1][j] > l[i][j]) l[i][j] = l[i - 1][j];
  23.                     if (r[i - 1][j] < r[i][j]) r[i][j] = r[i - 1][j];
  24.                 }
  25.                 if ((r[i][j] - l[i][j] + 1) * h[i][j] > ans) ans = (r[i][j] - l[i][j] + 1) * h[i][j];
  26.             }
  27.         }
  28.     cout << 3 * ans << endl;

  29.     return 0;
  30. }
复制代码


不理解怎么可以这样写

大佬能帮忙解释一下吗?
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-8-9 14:58:40 | 显示全部楼层
这段代码是用来解决悬线算法问题的。下面我逐行解释一下代码的逻辑:

1. 首先定义了一个二维数组 h 用来记录每个位置的高度,以及三个二维数组 l 、 r 和 a 。
2. 接下来输入了矩阵的行数和列数,并使用两层循环遍历矩阵的每个位置。
3. 在内层循环中,首先输入了矩阵中每个位置的字符,并将 h[ i][j] 、 r[ i][j] 和 l[ i][j] 初始化为 j ,表示当前位置的高度、最右边界和最左边界。
4. 然后使用两个循环判断当前位置和前一个位置是否都为字符 F ,如果是,则将当前位置的最左边界 l[ i][j] 更新为前一个位置的最左边界 l[ i][j-1] 。
5. 同样地,使用一个循环判断当前位置和后一个位置是否都为字符 F ,如果是,则将当前位置的最右边界 r[ i][j] 更新为后一个位置的最右边界 r[ i][j+1] 。
6. 接下来的两层循环用来计算每个位置的面积,并更新最大面积 ans 的值。
7. 在外层循环中,如果当前位置和上一个位置都为字符 F ,则更新当前位置的高度 h[ i][j] 为上一个位置的高度加一,并更新最左边界 l[ i][j] 为两者中较大的值,最右边界 r[ i][j] 为两者中较小的值。
8. 最后输出结果 3 * ans 。

总体来说,这段代码的目的是计算矩阵中由字符 F 组成的最大矩形的面积,并输出结果。具体的实现思路是通过动态规划的方式,先计算每个位置的最左边界和最右边界,再计算每个位置的高度,并更新最大面积。希望能够帮助你理解这段代码。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 14:58:46 | 显示全部楼层
本帖最后由 陶远航 于 2023-8-9 14:59 编辑

单调栈
先输入数据
处理处每个点往上一共有多少个连续的没有被画过的点
然后每一行f[i][0]和f[i][m + 1]要赋值一个超级小的数
为了让区间边界终止与此

然后顺序扫一遍找出每一个点
左边距离他最近的一个比他矮的点
然后倒叙扫一遍找出每一个点
右边距离他最近的一个比他矮的店
中间的就是它能够构成的矩阵
矩阵组成方式是
左边区间的长度(包括中间点) * 右边区间的长度(包括中间点) * 宽(也就是f[i][j])
累加起来输出就好了

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<stack>
  4. #define int long long

  5. using namespace std;
  6. const int Max = 1005;
  7. int f[Max][Max];
  8. int a[Max];
  9. int r[Max],l[Max];
  10. signed main()
  11. {
  12.         char c;
  13.         int n,m;
  14.         cin >> n >> m;
  15.         for(register int i = 1;i <= n;++ i)
  16.         {
  17.                 for(register int j = 1;j <= m;++ j)
  18.                 {
  19.                         cin >> c;
  20.                         if(c == '*')f[i][j] = 0;
  21.                         else
  22.                         f[i][j] = f[i - 1][j] + 1;
  23.                 }
  24.         }
  25.         for(int i = 1;i <= n;++ i)
  26.                 f[i][0] = f[i][m + 1] = -0x7fffffff;
  27.         int ans = 0;
  28.         for(register int i = 1;i <= n;++ i)
  29.         {
  30.                 stack<int>s1,s2;
  31.                 s1.push(1),s2.push(m);
  32.                 for(register int ii = 2,jj = m - 1;ii <= m + 1,jj >= 0;jj --,++ ii)
  33.                 {
  34.                         while(!s1.empty() && f[i][ii] < f[i][s1.top()])
  35.                         {
  36.                                 r[s1.top()] = ii;
  37.                                 s1.pop();
  38.                         }
  39.                         while(!s2.empty() && f[i][jj] <= f[i][s2.top()])
  40.                         {
  41.                                 l[s2.top()] = jj;
  42.                                 s2.pop();
  43.                         }
  44.                         s1.push(ii);s2.push(jj);
  45.                 }
  46.                 for(register int j = 1;j <= m;++ j)
  47.                         ans += (j - l[j]) * (r[j] - j) * f[i][j];
  48.         }
  49.         cout << ans << endl;
  50.         return 0;
  51. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 14:58:59 | 显示全部楼层
问题理解:

您想要理解悬线算法,并解释给定的代码(luogu P1950)。您希望得到对代码的逐行解释和算法的解释。

解决方案:

给定的代码实现了悬线算法(它可以用来解决一些矩阵相关的问题),并且似乎是为了解决题目P1950。

下面是对给定代码中每一行的逐行解释:
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 1010;
  4. int n, m, ans, h[MAXN][MAXN], l[MAXN][MAXN], r[MAXN][MAXN];
  5. char a[MAXN][MAXN];
复制代码
这部分代码包含了头文件引用和全局变量的声明,MAXN表示矩阵的最大大小。
  1. int main() {
  2.     ans = 0;
  3.     cin >> n >> m;
  4.     for (int i = 1; i <= n; i++) {
  5.         for (int j = 1; j <= m; j++) {
  6.             cin >> a[i][j];
  7.             h[i][j] = 1, r[i][j] = l[i][j] = j;
  8.         }
  9.         
  10.         for (int j = 2; j <= m; j++)
  11.             if (a[i][j] == 'F' && a[i][j - 1] == 'F')
  12.                 l[i][j] = l[i][j - 1];
  13.                
  14.         for (int j = m - 1; j > 0; j--)
  15.             if (a[i][j] == 'F' && a[i][j + 1] == 'F')
  16.                 r[i][j] = r[i][j + 1];
  17.     }
复制代码
该部分通过读取输入的矩阵大小以及每个位置上的字符构建了矩阵a。并且初始化了变量hlr

对于每一行,它检查相邻两个字符是否为'F',如果是,则将l数组中位置j的值更新为l[i][j - 1](记录了当前位置往左最远的'F'的位置)。随后,它从右向左遍历矩阵的最后一行,如果相邻两个字符都为'F',则将r数组中位置j的值更新为r[i][j + 1](记录了当前位置往右最远的'F'的位置)。
  1.     for (int i = 1; i <= n; i++) {
  2.         for (int j = 1; j <= m; j++) {
  3.             if (i > 1 && a[i][j] == 'F') {
  4.                 if (a[i - 1][j] == 'F') {
  5.                     h[i][j] = h[i - 1][j] + 1;
  6.                     if (l[i - 1][j] > l[i][j])
  7.                         l[i][j] = l[i - 1][j];
  8.                     if (r[i - 1][j] < r[i][j])
  9.                         r[i][j] = r[i - 1][j];
  10.                 }
  11.                 if ((r[i][j] - l[i][j] + 1) * h[i][j] > ans)
  12.                     ans = (r[i][j] - l[i][j] + 1) * h[i][j];
  13.             }
  14.         }
  15.     }
复制代码
这部分是算法的主要部分。它使用动态规划的思想,计算了每个位置(i,j)处悬线(由字符'F'组成)的高度h[i][j]而且记录了最远左端点和最远右端点分别为l[i][j]r[i][j]

对于每一个位置(i,j),如果当前位置上的字符是'F',并且该位置上方的字符也是'F',则更新高度h[i][j]、左端点l[i][j]和右端点r[i][j]。同时,如果通过当前位置构成的矩形面积大于之前的最大面积ans,则更新ans

最后,输出3 * ans

希望这个解释对您有所帮助。如果您还有其他问题,请随时提问。

球一个最佳答案谢谢啦!这对我非常重要!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-8-9 14:59:01 | 显示全部楼层
本帖最后由 学习编程中的Ben 于 2023-8-9 15:18 编辑

问题理解:

您想要了解悬线算法(Skyline Algorithm)的工作原理,并希望对给出的代码进行解释。您想知道如何理解这段代码,并希望得到完整修改后的代码。

解决方案:

悬线算法是一种处理二维数据结构的算法,常用于处理矩形区域的相关问题。该算法从左到右扫描矩形的每一列,记录每一列上最高的高度(高度由矩形的底部边界决定)。在此基础上,可以解决多种与矩形区域相关的问题,例如在给定矩形中找到最大的矩形面积。

现在我将对给出的代码进行解释,并提供修改后的完整代码。

  1. #include <iostream>
  2. using namespace std;

  3. const int MAXN = 1010;
  4. int n, m, ans, h[MAXN][MAXN], l[MAXN][MAXN], r[MAXN][MAXN];
  5. char a[MAXN][MAXN];

  6. int main() {
  7.     ans = 0;
  8.     cin >> n >> m;

  9.     // 读取矩形区域的字符表示
  10.     for (int i = 1; i <= n; i++) {
  11.         for (int j = 1; j <= m; j++) {
  12.             cin >> a[i][j];
  13.             h[i][j] = 1, r[i][j] = l[i][j] = j;
  14.         }
  15.         // 计算每一行内,连续的F字符所对应的最左边界和最右边界
  16.         for (int j = 2; j <= m; j++) {
  17.             if (a[i][j] == 'F' && a[i][j - 1] == 'F')
  18.                 l[i][j] = l[i][j - 1];
  19.         }
  20.         for (int j = m - 1; j > 0; j--) {
  21.             if (a[i][j] == 'F' && a[i][j + 1] == 'F')
  22.                 r[i][j] = r[i][j + 1];
  23.         }
  24.     }

  25.     // 使用悬线算法计算矩形区域的最大面积
  26.     for (int i = 1; i <= n; i++) {
  27.         for (int j = 1; j <= m; j++) {
  28.             if (i > 1 && a[i][j] == 'F') {
  29.                 if (a[i - 1][j] == 'F') {
  30.                     h[i][j] = h[i - 1][j] + 1;
  31.                     if (l[i - 1][j] > l[i][j])
  32.                         l[i][j] = l[i - 1][j];
  33.                     if (r[i - 1][j] < r[i][j])
  34.                         r[i][j] = r[i - 1][j];
  35.                 }
  36.                 if ((r[i][j] - l[i][j] + 1) * h[i][j] > ans)
  37.                     ans = (r[i][j] - l[i][j] + 1) * h[i][j];
  38.             }
  39.         }
  40.     }

  41.     cout << 3 * ans << endl;

  42.     return 0;
  43. }
复制代码

这段代码的作用是计算给定矩形区域中,由字符'F'组成的最大矩形面积。代码首先读取输入的矩形区域的大小,并将其表示为一个二维字符数组a。然后,通过两次遍历矩形区域的每一行,计算每个字符为'F'时,向上的连续'F'字符所对应的最左边界和最右边界。

接下来,代码使用悬线算法来计算矩形区域的最大面积。通过遍历每个字符,如果当前字符为'F'且上方的字符也为'F',则更新该位置的高度h、最左边界l和最右边界r。最后,通过计算每个'F'字符所对应的矩形面积,并更新最大面积ans

最后,输出结果为最大面积的3倍。

希望以上解释对您有所帮助。如果您还有其他问题,请随时提问。

如果回答对你有帮助,请给我一个最佳答案!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-9 14:59:32 | 显示全部楼层
学习编程中的Ben 发表于 2023-8-9 14:59
问题理解:

您想要了解悬线算法(Skyline Algorithm)的工作原理,并希望对给出的代码进行解释。您想知道 ...

出bug了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-8-9 15:00:59 | 显示全部楼层


不是,因为有 [ i ]恰好就是编辑器标签 [i][i][/i]
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-9 15:02:09 | 显示全部楼层
歌者文明清理员 发表于 2023-8-9 15:00
不是,因为有 [ i ]恰好就是编辑器标签 [i]

code标签没有封闭
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-8-9 15:04:05 | 显示全部楼层

哦,本来Ben写的是 [/code]111[/code],然后修复了脚本bug,现在改成了[code]111[code]
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2023-8-9 15:04:47 | 显示全部楼层
Snipaste_2023-08-09_15-04-31.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-9 15:05:43 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 15:06:09 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 15:14:55 | 显示全部楼层

不看看我的回答吗
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 15:18:51 | 显示全部楼层

啊啊啊啊啊啊,早就说不改了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 15:31:09 | 显示全部楼层
给个最佳答案呗!求你了!!!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 0 反对 1

使用道具 举报

 楼主| 发表于 2023-8-9 21:01:38 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 21:14:02 | 显示全部楼层
《绿题》
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-9 21:14:22 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-9 21:15:58 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-9 21:17:58 | 显示全部楼层

你告诉我悬线算法是什么就好了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-9 20:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表