本來以為之前寫過這個方法,今天又考了一道模板題,於是就記錄一下
懸線法是求解一類極大子矩陣問題的dp解法(maybe)
考試有人想出了\(o(n^2log^2n)\)的二分套二分做法,在此表示膜拜(即二分面積,列舉面積的約數)
從暴力方面想,對於乙個點,直接求出它向左右上的最遠距離再乘起來可能不對,所以只能求其中乙個的最遠距離
具體來說,定義\(u_\)表示從\((i,j)\)向上走能走出的最遠距離,\(l,r\)必須受到\(u\)的約束,所以定義為滿足\(u\)最大,矩陣合法的條件下可以向左/右走到的最遠點,那麼\(ans=max(u_*(r_-l_+1))\)
形象來看,可以理解為以\((i,j)\)這個點為下端的連續的1組成的一根豎線,這根線左右移動所形成的矩形區域即這個點對應的矩形,這些矩形面積的最大值即為答案
顯然\(u,l,r\)三個陣列可以\(o(n^2)\)遞推(見**),正確性:1.由上面的定義可知,所有的矩陣合法(是子矩陣) 2.最大的矩陣中一定存在一根等於矩形高度的懸線(該線可以左右移動形成該矩陣),如果不存在,即所有的懸線都比該矩陣高,那顯然它不是最大矩陣
#include#define n 1005
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,m,a[n][n],ans;
int up[n][n],l[n][n],r[n][n];
template void read(t &x)
int main()
} for(int i=1;i<=n;++i)
for(int j=2;j<=m;++j)
if(a[i][j]&&a[i][j-1])
l[i][j]=l[i][j-1];
for(int i=1;i<=n;++i)
for(int j=m-1;j>=1;--j)
if(a[i][j]&&a[i][j+1])
r[i][j]=r[i][j+1];
for(int i=1;i<=n;++i)
ans=max(ans,(r[i][j]-l[i][j]+1)*up[i][j]);
} }cout《習題:玉蟾宮(其實就是例題),棋盤製作(強行將高和寬取\(min\)即可保證為正方形)
懸線法DP總結
求滿足某種條件 如01交替 的最大矩形 正方形 先預處理出 ml i j mr i j mt i j 分別表示當前位置 i,j 能向左擴充套件到的最左邊的編號 能向右擴充套件到的最右邊的編號 能向上擴充套件到的最大高度。然後在做 dp 時,除第一行,每行根據上一行的狀態更新當前狀態,逐行掃一遍。複雜...
懸線法 有套路的DP
例題 p1169 zjoi2007 棋盤製作 西洋棋是世界上最古老的博弈遊戲之一,和中國的圍棋 象棋以及日本的將棋同享盛名。據說西洋棋起源於易經的思想,棋盤是乙個8 88 times 88 8大小的黑白相間的方陣,對應八八六十四卦,黑白對應陰陽。而我們的主人公小q,正是西洋棋的狂熱愛好者。作為乙個頂...
關於懸線法
懸線法的模板題,下面主要講懸線法 懸線法思路 懸線的定義,就是一條豎線,這條豎線要滿足上端點在整個矩形上邊界或者是乙個障礙點。然後以這條懸線進行左右移動,直到移至障礙點或者是矩陣邊界,進而確定這條懸線所在的極大矩陣。具體方法 先預處理 用陣列l,r記錄某點向左和向右能到達的最遠點的縱座標。用陣列up...