ACM算法:悬线法

来源:互联网 发布:linux 抓包文件 导出 编辑:程序博客网 时间:2024/06/05 08:07

悬线法的用途:针对求给定矩阵中满足某条件的极大矩阵,比如“面积最大的长方形、正方形”“周长最长的矩形等等”。可以满足在

时间复杂度为O(M*N)的要求,比一般的枚举高效的多,也易于理解。


悬线法思路:悬线法,悬线的定义,就是一条竖线,这条竖线要满足上端点在整个矩形上边界或者是一个障碍点。然后以这条悬线

进行左右移动,直到移至障碍点或者是矩阵边界,进而确定这条悬线所在的极大矩阵。也就是说,我们要针对矩阵中每个点进行求极

大矩阵的操作,所以我们需要Left[]数组存每个点能到达的最右位置,Right[]数组存放每个点能到达的最左位置,Up[]数组位置。

设置好这些数组之后,我们开始遍历矩阵中的每个点ves[i,j],把每个点和上一个点(ves[i-1][j])的Left和Right进行比较,分别取最大和

最小,Up则是上一个点的Up+1,进而求出面积进行比较。所以我们可以得到相关的递推公式。


递推公式:Up:Up[i][j] = Up[i-1][j] + 1 

Right:min(Right[i][j],RIght[i-1],[j])

Left::max(Left[i][j],Left[i-1][j])



在这里推荐一篇国家队的论文《极大化思想解决最大子矩阵问题》

http://blog.csdn.net/clover_hxy/article/details/50532289?locationNum=1&fps=1

这里全面讲解了极大化矩阵的思想,只要能看透,我相信对于悬线法的理解肯定会更加深入。只是没有例题,所以你进行理解后再

倒回来理解我的例题,印象会更加深刻。

例题解析:

题目:棋盘制作

国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源
于易经的思想,棋盘是一个8*8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。而我们的主人公小Q,
正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定
将棋盘扩大以适应他们的新规则。小Q找到了一张由N*M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种
颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。不过小Q还没有决定是找
一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他
希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。于是小Q找到了即将参加全
国信息学竞赛的你,你能帮助他么?

Input

第一行包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的N行包含一个N * M的01矩阵,表示这张矩形
纸片的颜色(0表示白色,1表示黑色)。

Output

包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋
盘的面积(注意正方形和矩形是可以相交或者包含的)。

Sample Input

3 31 0 10 1 01 0 0

Sample Output

46

Hint

N, M ≤ 2000


这是中文题,大家都看得懂,我这就不解释什么了,直接上代码

#include <iostream>#include <algorithm>using namespace std;const int Max = 2005;int ves[Max][Max], up[Max][Max], Left[Max][Max], Right[Max][Max];int temp1 = 1, temp2 = 1;int main(void){ios::sync_with_stdio(false);int N, M;cin >> N >> M;for(int i = 1; i <= N; i++)for (int j = 1; j <= M; j++) {cin >> ves[i][j];Left[i][j] = Right[i][j] = j;//初始化Right和Left,使他们值为点所在纵坐标up[i][j] = 1;//初始化up使其值为1}for (int i = 1; i <= N; i++)for (int j = 2; j <= M; j++)if (ves[i][j] == 1 - ves[i][j - 1])//判断相邻两个数是否不同Left[i][j] = Left[i][j - 1];//是,则for (int i = 1; i <= N; i++)for (int j = M - 1; j > 0; j--)if (ves[i][j] == 1 - ves[i][j + 1])Right[i][j] = Right[i][j + 1];for(int i = 1;i <= N; i++)for (int j = 1; j <= M; j++) {if (i > 1 && ves[i][j] == 1 - ves[i - 1][j]) {//递推公式Left[i][j] = max(Left[i][j], Left[i - 1][j]);Right[i][j] = min(Right[i][j], Right[i - 1][j]);up[i][j] = up[i - 1][j] + 1;}int A_instance = Right[i][j] - Left[i][j] + 1;//计算长度int B_instance = min(A_instance, up[i][j]);//算出长宽中较小的边,以计算正方形temp1 = max(temp1, B_instance * B_instance);//正方形面积temp2 = max(temp2, A_instance * up[i][j]);//长方形面积}cout << temp1 << endl << temp2 << endl;}


总结:悬线法也是针对一类问题——求极大矩阵的问题,学会之后又将是解决一类问题的利器,而起思路非常清晰,易于理解。

只是有时候变式比较多,需要多加练习进行熟练。



原创粉丝点击