参考:https://blog.csdn.net/twtsa/article/details/8120269

先给出题目来源:(洛谷)

1.p1387 最大正方形

2.P1169 棋盘制作

3.p2701 巨大的牛棚

4.p4147 玉蟾宫

5.P1578 奶牛浴场

......

悬线法,很好理解,就是悬一根线晃来晃去求最大子矩阵嘛!

思路转移方程也很简单:

if(满足^&%$!@#^%){
right[i][j]=min(right[i][j],right[i-][j]);
left[i][j]=max(left[i][j],left[i-][j]);
up[i][j]=up[i-][j]+;
}

下面解释一下:

right表示从(i,j)这个点出发向右能到达最远的距离

left和up差不多,一个向左,一个向上

关于初始化

for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
right[i][j]=left[i][j]=j,up[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(满足条件)
right[i][j]=right[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(满足条件)
left[i][j]=left[i][j+];

其实这个东西跟模板一样套就好了





【NO.1】最大正方形

【解法1】

  数据这么小,考虑暴力:

  维护矩阵二维前缀和,暴力枚举左上角和正方形的长,判断该块矩阵和是否为长*长,更新最大值

  复杂度:O(n^3)

  (很久以前以前写的代码,可能有点丑)

    #include<iostream>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,map[][];
int sum[][];
void pre(){
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
sum[i][j]=sum[i-][j]+sum[i][j-]-sum[i-][j-]+map[i][j];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&map[i][j]);
pre();
int ans=-;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
for(int l=;l<=min(n,m);l++){
int rx=i+l-,ry=j+l-;
if(i-+l>n||j-+l>m||sum[rx][ry]-sum[rx][j-]-sum[i-][ry]+sum[i-][j-]!=l*l) break;
if(ans<l) ans=l;
}
printf("%d",ans);
return ;
}

【缺点】但是如果n=5000之类稍微大一点的数据就GG了,所以接下来我们用悬线法解决这个问题

    #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
char chr=getchar(); int f=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,m,a[][];
int l[][],r[][],up[][],ans1,ans2;
int main(){
n=read(),m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
a[i][j]=read(),l[i][j]=r[i][j]=j,up[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]==a[i][j-]&&a[i][j]==) l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(a[i][j]==a[i][j+]&&a[i][j]==) r[i][j]=r[i][j+];//预处理
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
if(i>)
if(a[i][j]==a[i-][j]&&a[i][j]==){//满足条件
l[i][j]=max(l[i][j],l[i-][j]);
r[i][j]=min(r[i][j],r[i-][j]);
up[i][j]=up[i-][j]+;
}
int a=r[i][j]-l[i][j]+;
int b=min(a,up[i][j]);
ans1=max(b,ans1);//更新答案
}
cout<<ans1;
return ;
}

 有没有发现,其实就是模板里面把条件加上去就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=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,m,a[][];
int l[][],r[][],up[][],ans1,ans2;
int main(){
n=read(),m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
a[i][j]=read(),l[i][j]=r[i][j]=j,up[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]!=a[i][j-]) l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(a[i][j]!=a[i][j+]) r[i][j]=r[i][j+];
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
if(i>)
if(a[i][j]!=a[i-][j]){
l[i][j]=max(l[i][j],l[i-][j]);
r[i][j]=min(r[i][j],r[i-][j]);
up[i][j]=up[i-][j]+;
}
int a=r[i][j]-l[i][j]+;
int b=min(a,up[i][j]);
ans1=max(b*b,ans1);
ans2=max(a*up[i][j],ans2);
}
cout<<ans1<<"\n"<<ans2;
return ;
}

【NO.3】巨大的牛棚

还是模板?只不过读入的时候转换一下就好了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
char chr=getchar(); int f=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,T;
int a[][],l[][],r[][],u[][],ans;
int main(){
n=read();T=read();
for(int i=;i<=T;i++){int x=read(),y=read();a[x][y]=;}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
l[i][j]=r[i][j]=j,u[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
if(a[i][j]==&&a[i][j-]==) l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=n-;j>=;j--)
if(a[i][j]==&&a[i][j+]==) r[i][j]=r[i][j+];
for(int i=;i<=n;i++)
for(int j=;j<=n;j++){
if(i>&&a[i][j]==&&a[i-][j]==){
u[i][j]=u[i-][j]+;
l[i][j]=max(l[i-][j],l[i][j]);
r[i][j]=min(r[i-][j],r[i][j]);
}
int a=r[i][j]-l[i][j]+;
int b=min(a,u[i][j]);
ans=max(ans,b);
}
cout<<ans;
return ;
}

【NO.4】玉蟾宫

这些题几乎一样...都不想说什么了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
char chr=getchar(); int f=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,m;
char a[][];
int l[][],r[][],up[][],ans1,ans2;
int main(){
n=read(),m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
cin>>a[i][j];
l[i][j]=r[i][j]=j,up[i][j]=;
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]==a[i][j-]&&a[i][j]=='F') l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(a[i][j]==a[i][j+]&&a[i][j]=='F') r[i][j]=r[i][j+];
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
if(i>)
if(a[i][j]==a[i-][j]&&a[i-][j]=='F'){
l[i][j]=max(l[i][j],l[i-][j]);
r[i][j]=min(r[i][j],r[i-][j]);
up[i][j]=up[i-][j]+;
}
int a=r[i][j]-l[i][j]+;
int b=min(a,up[i][j]);
ans2=max(a*up[i][j],ans2);
}
cout<<ans2*;
return ;
}

当然,难的在这里:奶牛浴场 (上面看懂的同学可以挑战一下这题)

这里有题解噢~

占坑,别禁赛噢~

[DP专题]悬线法的更多相关文章

  1. hdu4328(经典dp用悬线法求最大子矩形)

    http://wenku.baidu.com/view/728cd5126edb6f1aff001fbb.html 关于悬线法,这里面有详解. 我当时只想到了记录最大长度,却没有想到如果连最左边和最右 ...

  2. BZOJ 1057: [ZJOI2007]棋盘制作( dp + 悬线法 )

    对于第一问, 简单的dp. f(i, j)表示以(i, j)为左上角的最大正方形, f(i, j) = min( f(i + 1, j), f(i, j + 1), f(i + 1, j + 1)) ...

  3. P1169 [ZJOI2007]棋盘制作 DP悬线法

    题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8 \times 88×8大小的黑白相间的方阵,对应八八六十四卦,黑白 ...

  4. P4147 玉蟾宫 二维DP 悬线法

    题目背景 有一天,小猫rainbow和freda来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地. 题目描述 这片土地被分成N*M个格子,每个格子里写着'R'或者'F ...

  5. [ZJOI2007]棋盘制作 悬线法dp 求限制下的最大子矩阵

    https://www.luogu.org/problemnew/show/P1169 第一次听说到这种dp的名称叫做悬线法,听起来好厉害 题意是求一个矩阵内的最大01交错子矩阵,开始想的是dp[20 ...

  6. BZOJ 1057: [ZJOI2007]棋盘制作 悬线法求最大子矩阵+dp

    1057: [ZJOI2007]棋盘制作 Description 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8*8大小的黑 ...

  7. 悬线法DP总结

    悬线法DP总结 问题模型 求满足某种条件(如01交替)的最大矩形(正方形) 思想 先预处理出\(ml[i][j],mr[i][j],mt[i][j]\),分别表示当前位置\((i,j)\)能向左扩展到 ...

  8. 悬线法——有套路的DP

    例题 P1169 [ZJOI2007]棋盘制作 题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8×88 \times ...

  9. 【题解】洛谷P1169 [ZJOI2007] 棋盘制作(坐标DP+悬线法)

    次元传送门:洛谷P1169 思路 浙江省选果然不一般 用到一个从来没有听过的算法 悬线法: 所谓悬线法 就是用一条线(长度任意)在矩阵中判断这条线能到达的最左边和最右边及这条线的长度 即可得到这个矩阵 ...

随机推荐

  1. 配置git使用ssh方式克隆gitlab的代码

    配置ssh key https://www.cnblogs.com/hafiz/p/8146324.html 配置host https://www.cnblogs.com/kaerxifa/p/109 ...

  2. .net 内嵌 GeckoWebBrowser (firefox) 核心浏览器

    引用nuget包: 注意:Geckofx45 nuget包必须是最后引用,否则初始化会出错 简单示例: using Gecko; using System; using System.Collecti ...

  3. JAVA学习总结-基础语法

    /** * 这篇文章供自己学习JAVA总结回顾使用 * 主要借鉴了马士兵老师的视频进行总结 * @author Kingram */ 标识符的概念和命名规则 JAVA常量---不可变的变量 程序的执行 ...

  4. Highcharts教程--把js代码从html中抽离出来,放到单独的一个js文件中。由html页面调用

    1.html页面写法 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  5. Linux之iptables(一、防火墙的概念)

    防火墙的概念 一.安全技术 入侵检测与管理系统(Intrusion Detection Systems):特点是不阻断任何网络访问,量化.定位来自内外网络的威胁情况,主要以提供报告和事后监督为主,提供 ...

  6. Flask - Flask的蓝图(BluePrint)

    目录 Flask - Flask的蓝图(BluePrint) 一. 初始Flask蓝图 进阶Flask蓝图 使用蓝图做一个增删改查 1.使用蓝图进行web应用搭建: 2.使用Flask蓝图,查看学生信 ...

  7. 一个电商项目的Web服务化改造3:改进方案の规范和约定、单表、单一职责

         最近一直在做一个电商项目,需要把原有单系统架构的项目,改造成基于服务的架构,SOA.      有点挑战,做完了,会有很大进步. 上一篇,我们描述了原有项目中的问题.  或者说是,本篇的基本 ...

  8. hdu2002 计算球体积【C++】

    计算球体积 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  9. GlobalSign 企业型SSL 证书

    GlobalSign 企业型SSL 证书   GlobalSign 企业型 SSL 证书属于OV SSL,进行严格的网站所有权的验证,企业真实身份验证,证书标识企业组织机构名称,增加信任度.提供40位 ...

  10. ubuntu添加开机自启和sysv-rc-conf

    此文ubuntu使用sysvinit,而非upstart UBUNTU添加开机自动启动程序方法 1. 开机启动时自动运行程序    Linux加载后, 它将初始化硬件和设备驱动, 然后运行第一个进程i ...