參考:https://blog.csdn.net/twtsa/article/details/8120269
先給出題目來源:(洛谷)
......
懸線法,很好理解,就是懸一根線晃來晃去求最大子矩陣嘛!
思路和轉移方程也很簡單:
if(滿足^&%$!@#^%){ right[i][j]=min(right[i][j],right[i-1][j]); left[i][j]=max(left[i][j],left[i-1][j]); up[i][j]=up[i-1][j]+1; }
下面解釋一下:
right表示從(i,j)這個點出發向右能到達最遠的距離
left和up差不多,一個向左,一個向上
關於初始化
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) right[i][j]=left[i][j]=j,up[i][j]=1; for(int i=1;i<=n;i++) for(int j=2;j<=m;j++) if(滿足條件) right[i][j]=right[i][j-1]; for(int i=1;i<=n;i++) for(int j=m-1;j>=1;j--) if(滿足條件) left[i][j]=left[i][j+1];
其實這個東西跟模板一樣套就好了
【NO.1】最大正方形
【解法1】
數據這么小,考慮暴力:
維護矩陣二維前綴和,暴力枚舉左上角和正方形的長,判斷該塊矩陣和是否為長*長,更新最大值
復雜度:O(n^3)
(很久以前以前寫的代碼,可能有點丑)
#include<iostream> #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; int n,m,map[105][105]; int sum[105][105]; void pre(){ for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+map[i][j]; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&map[i][j]); pre(); int ans=-1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int l=1;l<=min(n,m);l++){ int rx=i+l-1,ry=j+l-1; if(i-1+l>n||j-1+l>m||sum[rx][ry]-sum[rx][j-1]-sum[i-1][ry]+sum[i-1][j-1]!=l*l) break; if(ans<l) ans=l; } printf("%d",ans); return 0; }【缺點】但是如果n=5000之類稍微大一點的數據就GG了,所以接下來我們用懸線法解決這個問題
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read(){ char chr=getchar(); int f=1,ans=0; while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();} while(isdigit(chr)) {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();} return ans*f; } void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } int n,m,a[4005][4005]; int l[4005][4005],r[4005][4005],up[4005][4005],ans1,ans2; int main(){ n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(),l[i][j]=r[i][j]=j,up[i][j]=1; for(int i=1;i<=n;i++) for(int j=2;j<=m;j++) if(a[i][j]==a[i][j-1]&&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]&&a[i][j]==1) r[i][j]=r[i][j+1];//預處理 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(i>1) if(a[i][j]==a[i-1][j]&&a[i][j]==1){//滿足條件 l[i][j]=max(l[i][j],l[i-1][j]); r[i][j]=min(r[i][j],r[i-1][j]); up[i][j]=up[i-1][j]+1; } int a=r[i][j]-l[i][j]+1; int b=min(a,up[i][j]); ans1=max(b,ans1);//更新答案 } cout<<ans1; return 0; }
有沒有發現,其實就是模板里面把條件加上去就OK了,驚不驚喜!!!哈哈哈(其實好像下面大部分都是這樣的)比如下面這題
【NO.2】棋盤制作
一樣是套模板,改一下條件
注意到該題條件是10間隔分布,則if語句中內容應為:if(a[i][j]!=a[i-1][j])注意這里還有一個大前提就是i>1!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read(){ char chr=getchar(); int f=1,ans=0; while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();} while(isdigit(chr)) {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();} return ans*f; } void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } int n,m,a[4005][4005]; int l[4005][4005],r[4005][4005],up[4005][4005],ans1,ans2; int main(){ n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(),l[i][j]=r[i][j]=j,up[i][j]=1; 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++) for(int j=1;j<=m;j++){ if(i>1) if(a[i][j]!=a[i-1][j]){ l[i][j]=max(l[i][j],l[i-1][j]); r[i][j]=min(r[i][j],r[i-1][j]); up[i][j]=up[i-1][j]+1; } int a=r[i][j]-l[i][j]+1; int b=min(a,up[i][j]); ans1=max(b*b,ans1); ans2=max(a*up[i][j],ans2); } cout<<ans1<<"\n"<<ans2; return 0; }
【NO.3】巨大的牛棚
還是模板?只不過讀入的時候轉換一下就好了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read(){ char chr=getchar(); int f=1,ans=0; while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();} while(isdigit(chr)) {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();} return ans*f; } void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } int n,T; int a[1005][1005],l[1005][1005],r[1005][1005],u[1005][1005],ans; int main(){ n=read();T=read(); for(int i=1;i<=T;i++){int x=read(),y=read();a[x][y]=1;} for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) l[i][j]=r[i][j]=j,u[i][j]=1; for(int i=1;i<=n;i++) for(int j=2;j<=n;j++) if(a[i][j]==0&&a[i][j-1]==0) l[i][j]=l[i][j-1]; for(int i=1;i<=n;i++) for(int j=n-1;j>=1;j--) if(a[i][j]==0&&a[i][j+1]==0) r[i][j]=r[i][j+1]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ if(i>1&&a[i][j]==0&&a[i-1][j]==0){ u[i][j]=u[i-1][j]+1; l[i][j]=max(l[i-1][j],l[i][j]); r[i][j]=min(r[i-1][j],r[i][j]); } int a=r[i][j]-l[i][j]+1; int b=min(a,u[i][j]); ans=max(ans,b); } cout<<ans; return 0; }
【NO.4】玉蟾宮
這些題幾乎一樣...都不想說什么了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read(){ char chr=getchar(); int f=1,ans=0; while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();} while(isdigit(chr)) {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();} return ans*f; } void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } int n,m; char a[4005][4005]; int l[4005][4005],r[4005][4005],up[4005][4005],ans1,ans2; int main(){ n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ cin>>a[i][j]; l[i][j]=r[i][j]=j,up[i][j]=1; } for(int i=1;i<=n;i++) for(int j=2;j<=m;j++) if(a[i][j]==a[i][j-1]&&a[i][j]=='F') 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]&&a[i][j]=='F') r[i][j]=r[i][j+1]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(i>1) if(a[i][j]==a[i-1][j]&&a[i-1][j]=='F'){ l[i][j]=max(l[i][j],l[i-1][j]); r[i][j]=min(r[i][j],r[i-1][j]); up[i][j]=up[i-1][j]+1; } int a=r[i][j]-l[i][j]+1; int b=min(a,up[i][j]); ans2=max(a*up[i][j],ans2); } cout<<ans2*3; return 0; }
