参考: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. Windows如何正确的修改administrator用户名

    无论是服务器还是电脑.修改默认的administrator的用户总是好的.话不多少.直接上图 1.win+r  输入:gpedit.msc 2.按照我下图的圈圈找到重命名系统管理员账户并自定义 3.重 ...

  2. Miller Rabbin素数测试

    步骤 ①先写快速幂取模函数 ②MR算法开始 (1)传入两个参数一个是底数一个是n也就是幂数,如果n是一个合数那么可以判定,这个数一定不是素数 (2)然后开始寻找一个奇数的n去计算,如果最后满足a^d% ...

  3. ELK6 收集不同来源的日志并做区分

    https://blog.csdn.net/u010871982/article/details/79035317 使用filebeat替代logstash进行日志采集 https://blog.cs ...

  4. CODEVS 3500

    题目描述 输入3个数a,b,c,求a^b mod c=?输入描述          三个数a,b,c输出描述         一个数,即a^b mod c 的答案.样例输入5 10 9样例输出 4 基 ...

  5. 在Docker上构建mysql容器

    1.查看docker上的镜像是否有 mysql,如果没有下载则列表中没有  [root@holly holly]# docker images; 如果没有只会看到如下结构 REPOSITORY  TA ...

  6. C# 通俗说 内存的理解

    一.概念 堆栈是什么? 在说堆栈之前,先说说内存是神马? 内存:程序在运行的过程,电脑需要不断通过CPU进行计算,这个计算的过程会读取并产生运算的数据,这些数据需要一个存储容器存放.这个容器,这就是内 ...

  7. XOR Queries

    XOR Queries 时间限制: 1000ms   内存限制: 256M 描述 给出一个长度为n的数组C,回答m个形式为(L,R,A,B)的询问,含义为存在多少个不同的数组下标k∈[L,R]满足C[ ...

  8. hdu 4862KM&最小费用最大流

    /*最小K路径覆盖的模型,用费用流或者KM算法解决, 构造二部图,X部有N*M个节点,源点向X部每个节点连一条边, 流量1,费用0,Y部有N*M个节点,每个节点向汇点连一条边,流量1, 费用0,如果X ...

  9. CODEVS2144 砝码称重2 (哈希表)

    由于m很大,所以不能使用DP. 注意到n≤30,直接暴力2^n会TLE. 所以,将砝码平均分成两份,对一份进行一次暴力,用哈希表存下可能的结果. 对下一份再进行一次暴力,在哈希表中搜索剩余的砝码重量是 ...

  10. POJ 3304 segments 线段和直线相交

    Segments Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14178   Accepted: 4521 Descrip ...