懸線法DP總結
阿新 • • 發佈:2019-05-11
fine getchar() bool 模型 pan 獲得 pro 高度 href
懸線法DP總結
問題模型
求滿足某種條件(如01交替)的最大矩形(正方形)
思想
先預處理出\(ml[i][j],mr[i][j],mt[i][j]\),分別表示當前位置\((i,j)\)能向左擴展到的最左邊的編號、能向右擴展到的最右邊的編號、能向上擴展到的最大高度。
然後在做\(DP\)時,除第一行,每行根據上一行的狀態更新當前狀態,逐行掃一遍。復雜度\(O(n\times m)\)
題
[USACO5.3]巨大的牛棚Big Barn
洛谷題面
題意:求最大正方形
#include <cstdio> #define MAXN 1010 #define MAX(A,B) ((A)>(B)?(A):(B)) #define MIN(A,B) ((A)<(B)?(A):(B)) using namespace std; int n,t,ans; bool mp[MAXN][MAXN]; int ml[MAXN][MAXN],mr[MAXN][MAXN],mt[MAXN][MAXN]; int main() { scanf("%d %d", &n, &t); while(t--){ int x,y; scanf("%d %d", &x, &y); mp[x][y]=1; } for(register int i=1;i<=n;++i){ for(register int j=1;j<=n;++j){ ml[i][j]=mr[i][j]=j; mt[i][j]=1; } } for(register int i=1;i<=n;++i) for(register int j=2;j<=n;++j) if(mp[i][j-1]==0&&mp[i][j]==0) ml[i][j]=ml[i][j-1]; // 預處理出能向左擴展到的最左邊的編號 for(register int i=1;i<=n;++i) for(register int j=n-1;j>=1;--j) if(mp[i][j+1]==0&&mp[i][j]==0) mr[i][j]=mr[i][j+1]; // 預處理出能向右擴展到的最右邊的編號 for(register int i=1;i<=n;++i) for(register int j=2;j<=n;++j){ if(i>1&&mp[i-1][j]==0&&mp[i][j]==0){ // 除第一行,每行根據上一行的狀態更新當前狀態 ml[i][j]=MAX(ml[i][j], ml[i-1][j]); // 註意取MAX,獲得合法的ml mr[i][j]=MIN(mr[i][j], mr[i-1][j]); // 註意取MIN,獲得合法的mr mt[i][j]=mt[i-1][j]+1; } int w=MIN(mr[i][j]-ml[i][j]+1, mt[i][j]); // 因為是正方形 ans=MAX(ans, w); } printf("%d", ans); return 0; }
[ZJOI2007]棋盤制作
P1169 洛谷題面
題意:求最大01交替正方形、長方形
#include <cstdio> #define MAXN 2002 #define INF 0x3fffffff #define MAX(A,B) ((A)>(B)?(A):(B)) #define MIN(A,B) ((A)<(B)?(A):(B)) using namespace std; int n,m,ml[MAXN][MAXN],mr[MAXN][MAXN],mt[MAXN][MAXN]; int ans1, ans2; bool mp[MAXN][MAXN]; int main() { scanf("%d %d", &n, &m); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d", &mp[i][j]),ml[i][j]=mr[i][j]=j,mt[i][j]=1; for(int i=1;i<=n;++i) for(int j=2;j<=m;++j) if(mp[i][j]!=mp[i][j-1]) // 01擴展 ml[i][j]=MIN(ml[i][j-1], ml[i][j]); // 預處理出能向左擴展到的最左邊的編號 for(int i=1;i<=n;++i) for(int j=m-1;j>=1;--j) if(mp[i][j]!=mp[i][j+1]) // 01擴展 mr[i][j]=MAX(mr[i][j+1], mr[i][j]); // 預處理出能向右擴展到的最右邊的編號 for(int i=1;i<=n;++i) for(int j=2;j<=m;++j){ if(i>1&&mp[i][j]!=mp[i-1][j]){ // 除第一行,每行根據上一行的狀態更新當前狀態 ml[i][j]=MAX(ml[i][j], ml[i-1][j]); mr[i][j]=MIN(mr[i][j], mr[i-1][j]); mt[i][j]=mt[i-1][j]+1; } int w = mr[i][j] - ml[i][j]+1; int h = mt[i][j]; ans1 = MAX(MIN(w,h)*MIN(w,h), ans1); // 找出最大合法正方形 ans2 = MAX(w*h, ans2); // 找出最大合法矩形 } printf("%d\n%d", ans1, ans2); return 0; }
P4147 玉蟾宮
P4147 玉蟾宮
題意:毒瘤輸入,求最大正方形
#include <cstdio> #define MAXN 1010 #define MAX(A,B) ((A)>(B)?(A):(B)) #define MIN(A,B) ((A)<(B)?(A):(B)) using namespace std; int n,m,ans; bool mp[MAXN][MAXN]; int ml[MAXN][MAXN],mr[MAXN][MAXN],mt[MAXN][MAXN]; int read(){ char in=getchar(); while(in!='F'&&in!='R') in=getchar(); if(in=='F') return 1; return 0; } int main() { scanf("%d %d", &n, &m); for(register int i=1;i<=n;++i){ for(register int j=1;j<=m;++j){ mp[i][j]=read(); ml[i][j]=mr[i][j]=j; mt[i][j]=1; } } for(register int i=1;i<=n;++i) for(register int j=2;j<=m;++j) if(mp[i][j-1]==1&&mp[i][j]==1) ml[i][j]=ml[i][j-1]; for(register int i=1;i<=n;++i) for(register int j=m-1;j>=1;--j) if(mp[i][j+1]==1&&mp[i][j]==1) mr[i][j]=mr[i][j+1]; for(register int i=1;i<=n;++i) for(register int j=2;j<=m;++j){ if(i>1&&mp[i-1][j]==1&&mp[i][j]==1){ ml[i][j]=MAX(ml[i][j], ml[i-1][j]); mr[i][j]=MIN(mr[i][j], mr[i-1][j]); mt[i][j]=mt[i-1][j]+1; } int w=mr[i][j]-ml[i][j]+1; ans=MAX(ans, w*mt[i][j]); } printf("%d", 3*ans); return 0; }
懸線法DP總結