[DP专题]悬线法

reference:浅谈用极大化思想解决最大子矩阵问题

两种思路:

1.思想一:枚举所有的极大有效子矩形,如奶牛浴场

2.思想二:垂线法(后文介绍)

题目来源:

【最大全0子正方形】p1387 最大正方形

P1169 棋盘制作

【最大全0子正方形】p2701 巨大的牛棚

【最大子矩阵的和(1e3数量级)】p4147 玉蟾宫

P1578 奶牛浴场

本题坐标范围在3e4,所以无法dp,爆内存

reference

【缺陷:可拓展性不够(后文例题介绍),在点(最多N * M)密集的情况下表现较差】

#define N 5010
struct node{
    int x, y;
}a[N];
int L,W,n,ans=0;
bool cmp1(node a,node b){return a.x<b.x;}
bool cmp2(node a,node b){return a.y<b.y;}

#undef int
int main(){
#define int long long
    freopen("nainiu.txt","r",stdin);
    /*rd(L),rd(W),rd(n);
    rep(i,1,n)rd(a[i].x),rd(a[i].y);
    a[++n].x=0,a[n].y=0;
    a[++n].x=0,a[n].y=W;
    a[++n].x=L,a[n].y=0;
    a[++n].x=L,a[n].y=W;
    sort(a+1,a+n+1,cmp1);
    int up,down,v;
    rep(i,1,n){
        down=0,up=W,v=L-a[i].x;
        rep(j,i+1,n){
            if(v*(up-down)<=ans)break;
            ans=max(ans,(up-down)*(a[j].x-a[i].x));
            if(a[j].y>=a[i].y)up=min(up,a[j].y);
            else down=max(down,a[j].y);
        }
    }
    sort(a+1,a+n+1,cmp2);
    int l,r;
    rep(i,1,n){
        l=0,r=L,v=W-a[i].y;
        rep(j,i+1,n){
            if(v*(r-l)<=ans)break;
            ans=max(ans,(r-l)*(a[j].y-a[i].y));
            if (a[j].x>=a[i].x)r=min(r,a[j].x);
            else l=max(l,a[j].x);
        }
    }
    printf("%lld\n",ans);*/
    rd(L),rd(W),rd(n);
    rep(i,1,n)rd(a[i].x),rd(a[i].y);
    a[++n].x=0,a[n].y=0;
    a[++n].x=0,a[n].y=W;
    a[++n].x=L,a[n].y=0;
    a[++n].x=L,a[n].y=W;
    sort(a+1,a+n+1,cmp1);
    int up,down,v;
    rep(i,1,n){
        down=0,up=W,v=L-a[i].x;
        rep(j,i+1,n){
            if(v*(up-down)<=ans)break;
            ans=max(ans,(a[j].x-a[i].x)*(up-down));
            if(a[j].y>=a[i].y)up=min(up,a[j].y);
            else down=max(down,a[j].y);
        }
    }
    sort(a+1,a+n+1,cmp2);
    int l,r;
    rep(i,1,n){
        l=0,r=L,v=W-a[i].y;
        rep(j,i+1,n){
            if(v*(r-l)<=ans)break;
            ans=max(ans,(a[j].y-a[i].y)*(r-l));
            if(a[j].x>=a[i].x)r=min(r,a[j].x);
            else l=max(l,a[j].x);
        }
    }
    printf("%lld\n",ans);
    return 0;
}
/*
10 10
3
3 0
8 2
3 9
*/
//72

P1169 棋盘制作

我怎么写都只有60pts
题解

最大子矩阵的和

/*
reference:

translation:

solution:
    思路:我们知道一种求最大子段和的方法(什么你不知道?),就是O(n)遍历这个一维的数组,把当前遍历的数加入一个变量(tmp),在这个过程中记录最大值,如果这个变量变成负数,
那么就把这个变量置零,继续往下遍历。
为什么呢?
如果我们加入的这个数是一个正数,那正和我们意(我们意是什么鬼),因为正数可以让变量(tmp)更大,我们需要的就是一个最大值,如果加入的数是一个负数的话,分两种情况
1、tmp >= 0
        这样的话对于后面加入的数来说,我们前面所加的数是有意义的,因为变量还是一个正数(虽然减小了),它仍可以使得后面加入的数变大(哲学的声音?)
2、tmp < 0
        这样对于后面加入的数来说,我们前面所加的数毫无意义,它使得后面的数反而更小了,所以我们就不要前面的数了(一脸嫌弃),将tmp置零。

那么给你一个二维数组,求一个最大的子矩阵,和这个有什么关系呢? 一维数组 == n*1*1的二维矩阵
这么一看我们好像已经完成了对于一个特殊二维矩阵求最大子矩阵和。

那么对于题目给出的二维矩阵,我们可以转换为我们的特殊矩阵。我们枚举i、j,表示将i~j行看成一维数组,我们将a[i][k]+=a[j][k](对应位置相加),对a[i]这个一维数组求最大字段和
trigger:

note:
    *
record:

date:
    2019.08.20
*/
int a[505][505];
int dp[510][510];

int n;
int main(){
    while(~scanf("%d",&n)){
        rep(i,1,n)
            rep(j,1,n)
                rd(a[i][j]);
        memset(dp,0,sizeof(dp));
        rep(i,1,n)
            rep(j,1,n)
                dp[i][j]=dp[i-1][j]+a[i][j];
        rep(i,1,n){
            rep(j,1,n)
                printf("%d ",dp[i][j]);
            puts("");
        }
        int sum=0,ans=-0x3f3f3f3f;
        dwn(i,n,1){
            rep(j,1,i-1){
                sum=0;
                rep(k,1,n){
                    sum+=dp[i][k]-dp[j][k];
                    if(sum>ans)
                        ans=sum;
                    if(sum<0)
                        sum=0;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
/*
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
*/
//15

最大全0子正方形(的面积)

/*
reference:

translation:

solution:
    DP的思路,我们假设f(i,j)表示的是以i,j为右下角顶点的最大子正方形的边长。
这样初始条件f(1,j) f(i,1)即第一行和第一列分别都是自己的值的相反数(因为是0的子矩阵而不是1 啦) 

如果a(i,j)为1,
    更新f[i][j]=0(说明以ij为右下角顶点的最大子正方形的边长为0),
如果f(i,j)==1,
    f(i,j)更新为min(f(i-1,j),f(i,j-1),f(i-1,j-1))+1
trigger:

note:
    *
record:

date:
    2019.09.03
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dwn(i,a,b) for(int i=a;i>=b;--i)
template <typename T> inline void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;}
inline void write(int n){if(n==0)return;write(n/10);putchar(n%10+'0');}
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,u) for(int i=head[u];i;i=e[i].next)

#define N 1010

int n,maxx;
int a[N][N],f[N][N];

#undef int
int main(){
#define int long long
    #ifdef WIN32
    freopen("max_juzhen.txt","r",stdin);
    #endif
    rd(n);
    rep(i,1,n)
        rep(j,1,n){
            rd(a[i][j]);
        }
    rep(i,1,n)f[i][1]=-a[i][1];
    rep(i,1,n)f[1][i]=-a[1][i];
    rep(i,1,n){
        rep(j,1,n){
            if(a[i][j])f[i][j]=0;
            else {
                f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;
                maxx=max(maxx,f[i][j]);
            }
        }
    }
    printf("%lld",maxx*maxx);
    return 0;
}
/*
5
0 1 0 1 0
0 0 0 0 0
0 0 0 0 1
1 0 0 0 0
0 1 0 0 0
*/
//9 

原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11634702.html

时间: 2024-10-11 03:45:41

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

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

1057: [ZJOI2007]棋盘制作 Description 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8*8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳.而我们的主人公小Q,正是国际象棋的狂热爱好者.作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则.小Q找到了一张由N*M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一.小Q想在这种纸中裁减

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)) + 1 (假如(i, j)和右边和下边不冲突) 第二问就是经典的悬线法解决最大子矩阵了, 维护悬线H[i][j], 左边右边延伸的最长距离.先一行一行求出这一行的L, R, 然后再从上往下扫, 维护H, L, R 写完我才发现我脑残了...最大的正方形一定是在最大子矩阵里面啊...所以其实不用dp.

【bzoj1057】【ZJOI2007】【棋盘制作】【悬线法+dp】

Description 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8*8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳.而我们的主人公小Q,正是国际象棋的狂热爱好者.作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则.小Q找到了一张由N*M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一.小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可

悬线法——有套路的DP

例题 P1169 [ZJOI2007]棋盘制作 题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8×88 \times 88×8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳. 而我们的主人公小Q,正是国际象棋的狂热爱好者.作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则. 小Q找到了一张由N×MN \times MN×M个正方形的格子组成的矩形纸片,每个

bzoj1057: [ZJOI2007]棋盘制作(悬线法)

题目要求纵横坐标和奇偶性不同的点取值不同,于是我们把纵横坐标和奇偶性为1的点和0的点分别取反,就变成经典的最大全1子矩阵问题了,用悬线法解决. #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> using namespace std; const int maxn=2010,inf=1e9; int n,m,ans1,ans2; int h[maxn],mp[max

BZOJ_3039_玉蟾宫_(动态规划+悬线法)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3039 n*m的矩阵由R和F组成,求全是F的子矩阵的大小的三倍. 分析 悬线法: 浅谈用极大化思想解决最大子矩形问题--王知昆 l[x][y]表示点(x,y)在它那一行最多能扩展到左边的位置. r[x][y]表示点(x,y)在它那一行最多能扩展到右边的位置. 每一行分别预处理l与r. 在做dp的时候:如果点(x,y)可以取,那么h[x][y]=h[x-1][y]+1,l[x][y]=max(l

悬线法刷题记录

最近学习了悬线法,用极大化思想解决最大子矩阵问题,一种dp问题,留个记录…… 讲的特别好的一个博客:极大化思想解决最大子矩阵问题 例题: P1169 [ZJOI2007]棋盘制作 代码如下: 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <vector> 6 #define rep(x, l, r)

Codevs 1159 最大全0子矩阵 悬线法!!!!

1159 最大全0子矩阵 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在一个0,1方阵中找出其中最大的全0子矩阵,所谓最大是指O的个数最多. 输入描述 Input Description 输入文件第一行为整数N,其中1<=N<=2000,为方阵的大小,紧接着N行每行均有N个0或1,相邻两数间严格用一个空格隔开. 输出描述 Output Description 输出文件仅一行包含一个整数表示要求的最大的全零子矩阵中零的个数.

BZOJ 3039 玉蟾宫 悬线法

题目大意:给出一张地图,求出这张地图中最大的子矩阵,使得这个子矩阵不包含字母'R'. 思路:简单的悬线法求最大子矩阵,还是不带权值的,很好求.好久没写悬线了,复习一下. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 1010 using namespace std; int m,n; bool map[MAX][M