悬线法的用途:针对求给定矩阵中满足某条件的极大矩阵,比如“面积最大的长方形、正方形”“周长最长的矩形等等”。可以满足在
时间复杂度为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
这里全面讲解了极大化矩阵的思想,只要能看透,我相信对于悬线法的理解肯定会更加深入。只是没有例题,所以你进行理解后再
倒回来理解我的例题,印象会更加深刻。
例题解析:
题目:棋盘制作
Input
3 3 1 0 1 0 1 0 1 0 0
4 6
这是中文题,大家都看得懂,我这就不解释什么了,直接上代码
#include
#include
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;
}
总结:悬线法也是针对一类问题——求极大矩阵的问题,学会之后又将是解决一类问题的利器,而起思路非常清晰,易于理解。
只是有时候变式比较多,需要多加练习进行熟练。