zoukankan      html  css  js  c++  java
  • 【LGOJ4147】玉蟾宫

    一片土地被分成N*M个格子,每个格子里写着R'或者"'F'
    R代表这块土地被赐予了rainbow , F代表这块土地被赐予了freda
    现在freda要找一块矩形土地,要求这片土地都标着'F'并且面积最大
    输出最大的符合要求的土地的面积*3
    (原题面中是指有三个人每个人给你S两银子,其实就是乘三……)
    

    考场现学“悬线法”……
    说实话,这种方法很难想到,但是十分巧妙

    先讲一下另一种可以通过此题的不优算法:
    我们把此题抽象为一个01矩阵,要求找到最大的全1子矩阵
    由于答案具有某种意义上的单调性(如果有面积为S的矩阵符合,那么其约数大小的矩阵也符合)
    那么我们二分面积,每次check是否存在这么大的矩阵

    首先使用二维前缀和,统计出任意矩阵中1的个数,以及预处理约数
    然后对于每个我们需要check的mid,(n^2)枚举它的位置
    如果它所在的位置正好有面积那么多1,就说明存在

    前缀和和check是(Theta{(n^2)})
    预处理约数个数,以及二分面积都是(Theta{(log^{2} m)})的(m是最大的面积)
    所以总复杂度是(Theta{(n^2log^2 m)}),显然会T
    在随机数据下,我们把m设小一点即可过……

    这里介绍一下玄学的悬线法——

    如下图,它是一个01矩阵(灰色为0,白色为1)

    [ ext{tqr又乱画了一个图} ]

    我们在这张图上模拟实现一下这个算法:

    这个算法的朴素思路是什么呢?选取一列连续的1,定为所要扩展的矩形的高

    [ ext{如图,这个粉色块即将扩展} ]

    然后我们保持这个高度不变,让这个矩形向两边扩展!

    就这样,我们得到了一个面积为5的符合条件的矩形!

    然后我们把它的高度增加一点点……

    继续扩展

    现在得到的是一个面积为4的矩形……

    同理,继续进行操作,我们得到的就是面积为6的矩形

    简单地看一下图,可知这个矩形已经是最优的了(虽然不是唯一最优的)

    那么我们就得到了它的朴素思路:

    1. 确定一个连续的高
    2. 将其向左右扩展,更新答案

    但是这样的时间复杂度是(Theta{(n^3)})的,我们考虑下怎么用dp的思路优化它……

    首先,我们需要以下的值:

    1. 一个点往上能扩展到哪里(u数组),定为高
    2. 一个点往左能扩展到哪里(l数组)
    3. 一个点往右能扩展到哪里(r数组)

    很显然的,l和r不需要对于每个点(Theta{(n)})地求,它可以由同一行的左(右)一个格子转移过来

    for(register int i=1;i<=n;++i)
    	for(register int j=2;j<=m;++j)
    		if(a[i][j-1]&&a[i][j]) l[i][j]=l[i][j-1];
    for(register int i=1;i<=n;++i)
    	for(register int j=m-1;j>=1;--j)
    		if(a[i][j+1]&&a[i][j]) r[i][j]=r[i][j+1];
    

    这样,我们就(Theta{(n^2)})地预处理出了每个点的l和r

    等等,但这样转移的话,不会出问题吗???

    这样的一个矩形,如果它的高被更新之后,l和r怎么更新?

    看到,棕色的部分阻挡了下面扩展的路径,而且大矩形的l和r正好是之前的l和r与这一行的l和r的最大值和最小值!(拒绝感性理解)

    所以当u有转移的时候,l和r也会跟着转移(压缩范围)

    其中l会往右靠(如果有障碍的话),r会往左靠(同理),于是l取max,r取min

    for(register int i=1;i<=n;++i)
    {
    	for(register int j=1;j<=m;++j)
    	{
    		if(i-1&&a[i-1][j]&&a[i][j])
    		{
    			u[i][j]=u[i-1][j]+1;
    			l[i][j]=max(l[i][j],l[i-1][j]);
    			r[i][j]=min(r[i][j],r[i-1][j]);
    		}
    		ans=max(ans,(r[i][j]-l[i][j]+1)*u[i][j]);
    	}
    }
    

    其实到这里,这个算法就结束了……
    时间复杂度非常优秀,自己速速理解一下?

    最后放上本题代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m,ans;
    bool a[1005][1005];
    char c[2];
    int u[1005][1005],l[1005][1005],r[1005][1005];
    
    template<class T>inline void read(T &res)
    {
    	char c;T flag=1;
    	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
    	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    
    int main()
    {
    	read(n);read(m);
    	for(register int i=1;i<=n;++i)
    	{
    		for(register int j=1;j<=m;++j)
    		{
                scanf("%s",c);
    			if(c[0]=='F') a[i][j]=1;
    			else a[i][j]=0;
    			if(a[i][j]) u[i][j]=1,l[i][j]=r[i][j]=j;
    		}
    	}
    	for(register int i=1;i<=n;++i)
    		for(register int j=2;j<=m;++j)
    			if(a[i][j-1]&&a[i][j]) l[i][j]=l[i][j-1];
    	for(register int i=1;i<=n;++i)
    		for(register int j=m-1;j>=1;--j)
    			if(a[i][j+1]&&a[i][j]) r[i][j]=r[i][j+1];
    	for(register int i=1;i<=n;++i)
    	{
    		for(register int j=1;j<=m;++j)
    		{
    			if(i-1&&a[i-1][j]&&a[i][j])
    			{
    				u[i][j]=u[i-1][j]+1;
    				l[i][j]=max(l[i][j],l[i-1][j]);
    				r[i][j]=min(r[i][j],r[i-1][j]);
    			}
    			ans=max(ans,(r[i][j]-l[i][j]+1)*u[i][j]);
    		}
    	}
    	printf("%d
    ",ans*3);
    	return 0;
    }
    
  • 相关阅读:
    6月23号 java方法 什么是方法?
    6月21号 Java流程控制 练习
    6月21号 Java流程控制 break continue
    6月21号 Java流程控制 增强for
    6月19号 Java流程控制 循环结构for练习
    6月18号 Java流程控制 循环结构for
    qbot
    clion 2019.2
    raptor
    字符串练习
  • 原文地址:https://www.cnblogs.com/tqr06/p/11635676.html
Copyright © 2011-2022 走看看