动态规划之悬线法

前言

  • 悬线法是一种很好的,可以解决下面这类题型
  • 从矩阵中找到一个满足某条件的最大矩阵
  • 其思想是极大子矩阵。

悬线法直接搞

套路

悬线如下: 在这里插入图片描述

状态表示: 定义 h [ i ] [ j ] h[i][j] h[i][j] 为从 ( i , j ) (i,j) (i,j) 的上面挂到 ( i , j ) (i,j) (i,j) 的一条悬线最长长度,定义 l [ i ] [ j ] , r [ i ] [ j ] l[i][j],r[i][j] l[i][j],r[i][j] 为从这条悬线能往左/右延申到的左右边界。
状态转移:
t m p tmp tmp ( i , j ) (i,j) (i,j) 能到的最左边,若一条悬线能从 ( i , j ) (i,j) (i,j) 的上面挂到 ( i , j ) (i,j) (i,j) ,则: l [ i ] [ j ] = m a x ( t m p , l [ i − 1 ] [ j ] ) l[i][j]=max(tmp,l[i-1][j]) l[i][j]=max(tmp,l[i1][j])
t m p tmp tmp ( i , j ) (i,j) (i,j) 能到的最右边,若一条悬线能从 ( i , j ) (i,j) (i,j) 的上面挂到 ( i , j ) (i,j) (i,j),则: r [ i ] [ j ] = m i n ( t m p , r [ i − 1 ] [ j ] ) r[i][j]=min(tmp,r[i-1][j]) r[i][j]=min(tmp,r[i1][j])
若一条悬线能从 ( i , j ) (i,j) (i,j) 的上面挂到 ( i , j ) (i,j) (i,j) h [ i ] [ j ] = h [ i − 1 ] [ j ] h[i][j]=h[i-1][j] h[i][j]=h[i1][j]
若不能,考虑是否能从 ( i , j ) (i,j) (i,j) 令其一条直线,或不能。
初始值: 若无法有一条到达 ( i , j ) (i,j) (i,j) 的悬线也无法从 ( i , j ) (i,j) (i,j) 令其一条悬线,则 h [ i ] [ j ] = 0 , l [ i ] [ j ] = 1 , r [ i ] [ j ] = m h[i][j]=0,l[i][j]=1,r[i][j]=m h[i][j]=0l[i][j]=1,r[i][j]=m h [ 0 ] [ j ] = 0 , l [ 0 ] [ j ] = 1 , r [ 0 ] [ j ] = m h[0][j]=0,l[0][j]=1,r[0][j]=m h[0][j]=0,l[0][j]=1,r[0][j]=m
维护方式: 不同的题有不同的维护方式,但都是维护 h , l , r h,l,r h,l,r 这三个东西。

例题1:最大只包含1的矩形

例题链接

  • 题目描述: 一个 n × m n\times m n×m 的矩阵,每个位置有 F 或 R 。找到一个只包含 F 的最大矩阵。输出该最大矩形的面积 × 3 \times 3 ×3。n,m<=1000
#include<bits/stdc++.h>
using namespace std;
int h[2005][2005],l[2005][2005],r[2005][2005];
char a[2005][2005];
int main() {
	int n,m,ans=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)cin>>a[i][j];
	}
	for(int j=1;j<=m;j++){
		l[0][j]=1;
		r[0][j]=m;
		h[0][j]=0;
	}
	for(int i=1; i<=n; i++) {
		int tmp=1;
		for(int j=1; j<=m; j++) {
			if(a[i][j]=='F') {
                h[i][j]=h[i-1][j]+1;
				l[i][j]=max(tmp,l[i-1][j]);
			}else{
				h[i][j]=0;
				tmp=j+1;
				l[i][j]=1;
			}	
		}
		tmp=m;
		for(int j=m;j>=1;j--){
			if(a[i][j]=='F'){
				r[i][j]=min(tmp,r[i-1][j]);
			}else{
				tmp=j-1;
				r[i][j]=m;
			}
		}
		for(int j=1;j<=m;j++)ans=max(ans,h[i][j]*(r[i][j]-l[i][j]+1));
	}
	cout<<ans*3;
	return 0;
}

例题2:最大只包含1矩阵(0可以在边界)

  • 题目描述: 一个只含有0和1 n × m n\times m n×m 的矩阵,找到一个只包含 1 的最大子矩阵(0可以在边界上)。输出该最大矩形的面积 。n,m<=2000
#include<bits/stdc++.h>
using namespace std;

int h[2005][2005],l[2005][2005],r[2005][2005],a[2005][2005];
int main() {
	int n,m,ans=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)cin>>a[i][j];
	}
	for(int j=1;j<=m;j++){
		l[0][j]=1;
		r[0][j]=m;
		h[0][j]=0;
	}
	for(int i=1; i<=n; i++) {
		int tmp=1;
		for(int j=1; j<=m; j++) {
			if(a[i][j]==1) {
                h[i][j]=h[i-1][j]+1;
				l[i][j]=max(tmp,l[i-1][j]);
			}else{
				h[i][j]=0;
				tmp=j;
				l[i][j]=1;
			}	
		}
		tmp=m;
		for(int j=m;j>=1;j--){
			if(a[i][j]==1){
				r[i][j]=min(tmp,r[i-1][j]);
			}else{
				tmp=j;
				r[i][j]=m;
			}
		}
		for(int j=1;j<=m;j++)ans=max(ans,h[i][j]*(r[i][j]-l[i][j]+1));
	}
	cout<<ans;
	return 0;
}

例题3:最大的01交错矩阵

例题链接

  • 题目描述: 在一个 n × m n\times m n×m 的只包含 0 和 1 的矩阵里找出一个只包含 01 交错的最大正方形和最大矩形矩形,输出该最大正方形和最大矩形的面积。n,m<=2000
  • 问题分析: 与上题转移方程基本类似,只不过 t m p tmp tmp 的和悬线高度的维护方式有一点点差异。
  • 【注意】正方形 = m a x ( m i n ( h [ i ] [ j ] , r [ i ] [ j ] − l [ i ] [ j ] + 1 ) × m i n ( h [ i ] [ j ] , r [ i ] [ j ] − l [ i ] [ j ] + 1 ) ) max(min(h[i][j],r[i][j]-l[i][j]+1)\times min(h[i][j],r[i][j]-l[i][j]+1)) max(min(h[i][j],r[i][j]l[i][j]+1)×min(h[i][j],r[i][j]l[i][j]+1))
#include<bits/stdc++.h>
using namespace std;

int h[2005][2005],l[2005][2005],r[2005][2005],a[2005][2005];
int main() {
	int n,m,ans1=0,ans2=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)cin>>a[i][j];
	}
	for(int j=1;j<=m;j++){
		l[0][j]=1;
		r[0][j]=m;
		h[0][j]=0;
		a[0][j]=-1;
	}
	for(int i=1;i<=n;i++)a[i][0]=a[i][m+1]=-1;
	for(int i=1; i<=n; i++) {
		int tmp=1;
		for(int j=1; j<=m; j++) {
			if(a[i][j]==a[i][j-1])tmp=j;
			if(a[i][j]!=a[i-1][j]) {
                h[i][j]=h[i-1][j]+1;
				l[i][j]=max(tmp,l[i-1][j]);
			}else{
				h[i][j]=1;
				l[i][j]=tmp;
			}	
		}
		tmp=m;
		for(int j=m;j>=1;j--){
			if(a[i][j]==a[i][j+1])tmp=j;
			if(a[i][j]!=a[i-1][j]) {
				r[i][j]=min(tmp,r[i-1][j]);
			}else{
				h[i][j]=1;
				r[i][j]=tmp;
			}	
		}
		for(int j=1;j<=m;j++)ans1=max(ans1,min(h[i][j],(r[i][j]-l[i][j]+1))*min(h[i][j],(r[i][j]-l[i][j]+1)));
		for(int j=1;j<=m;j++)ans2=max(ans2,h[i][j]*(r[i][j]-l[i][j]+1));
	}
	cout<<ans1<<endl<<ans2;
	return 0;
}

题单链接

例题4:和为某范围之内的矩阵

例题链接

极大子矩阵思想

例题1:最大的只包含可行点的矩阵

例题链接

  • 题目描述: 一个 n × m n\times m n×m 的矩阵,有 q 个非可行点。找到一个只包含可行点的最大子矩阵(非可行点可以在边界上)。输出该最大矩形的面积 。n,m<=30000,q<=5000。
  • 问题分析: n 与 m 过大,显然无法用传统的悬线法来做了。
    学习链接

例题4:满足某条件的矩阵数量

  • 题目描述: 在一个 n × m n\times m n×m 的矩阵中有 q 个点,问有多少个连续子矩阵至少包含 k 个点。n,m<=3e3,k<=10。
  • 问题分析:
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值