jzoj3672. 【JSOI2014】拼图

Description

JYY最近迷上了拼图游戏。作为一个计算机科学家,JYY有一套黑白色的拼图,他希望通过合理的拼接,使得拼出的最终图案中,能包含面积最大的全白色子矩形。JYY一共有S块拼图,并且由1到S编号。编号为i的拼图是一个N行列的方格矩形,每个方格都为黑色或者白色。一开始JYY将他的这S块拼图按照编号顺序左右相连依次放在桌上拼成了一个N行M列的大矩形。之后JYY发现,可以通过改变这S块拼图的连接次序,使得拼成的N行M列的大矩形中,最大全白子矩形面积变大。现在JYY想知道,怎么拼才能得到最大的全白子矩形呢?请你帮助他计算出最佳的拼接方案。

Input

第一行包含一个整数T,代表测试数据的组数,接下来按顺序描述了每组测试数据。
每组测试数据的第一行包含两个整数S和N。
接下来S组输入,第i组对应编号为i的拼图。
在第i组输入中,第一行包含一个整数 WiWi ;
接下来N行描述一个N行 WiWi 列的0/1矩形;
其中第x行y列为0则表示该拼图对应位置的颜色是白色,反之则为黑色。

Output

对于每组数据输出一行包含一个整数ans,表示最大可能的全白色子矩形的面积。

Sample Input

1
3 4
4
1001
0000
0010
1001
3
000
010
000
011
2
00
10
01
00

Sample Output

6

Data Constraint

在这里插入图片描述

赛时

比赛想出了个 n 2 ∗ m n^2*m n2m的做法。
由于空间不知道怎么存,导致boom。

题解

我们发现这题有一个非常有用的限制: n ∗ m < = 1 0 5 n*m<=10^5 nm<=105
于是我们可以想到一个神奇的做法:

  • n < = n ∗ m n<=\sqrt{n*m} n<=nm
    • 我们考虑从n下手。
    • 如果是一整块的话,可以直接求出里面最大的方块。(利用前缀和)
    • 反之,如果是拼起来的话,那么我们可以枚举一个上界,一个下界。时间是 O ( n ) O(n) O(n)的。然后由于确定了上下界,那么我们只需要理会某一块的左边和右边在这个上下界之间的最大方块。(同样用到前缀和来维护)
    • 然后就可以求最大值合并。
    • 时间是 O ( n 2 ∗ m ) O(n^2*m) O(n2m)
  • m < = n ∗ m m<=\sqrt{n*m} m<=nm
    • 这时候就不太好从n下手了。那么换一种枚举方法就OK了。
    • 如果是一整块的话,可以换一下枚举顺序。同样可以快速求出。(当然前缀和)
    • 反之,如果是拼起来的话,我们考虑先枚举当前要求的方块的行的长度 k k k。然后枚举上界。由于知道了行长度为 k k k,上界为 l l l,那么当下界 r r r往下走的时候,是只加不减的。所以说可以往下慢慢地移动下界。
    • 具体的话就是 r r r往下挪一格,那么就可以用上面的方法求出上界为 l l l,下界为 r r r的答案。然后答案是否大于 k ∗ ( r − l + 1 ) k*(r-l+1) k(rl+1)是就往下挪,否则就不动了。如果 l > r l>r l>r那么也把 r r r挪到 l l l那里。这样就可以完美解决。
    • 答案则在下界往下挪的时候更新即可。
    • 时间是 O ( m ∗ n ∗ m ) O(m*n*m) O(mnm)
  • 亿点问题:
    • 在考虑记录空间的时候需要把一整块的拼图压到一条链上,这样存比较方便且快。
    • 当然vector也可以。但我不费用。

细节巨多。调了好久啊。

代码
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=1000001;

int T,s,n,len[maxn];
char ss[maxn]; 
int map[maxn],st[maxn],en[maxn];
int sum[maxn];
int q[3][maxn*2],op[maxn];
int d[3][maxn];
		
int co(int x,int y,int z)
{
	if (y==0 || x==0) return 0;
	return (x-1)*z+y;
}
int pd(int x,int y)
{
	if (y==0) return 0;
	return x+y;
}

int find(int l,int r)
{
	int ans=0;
	for (int i=1;i<=s;i++)
	{
		int k=0;
		for (int j=1;j<=len[i];j++)
		{
			if (op[pd(st[i]-1,co(r,j,len[i]))]-op[pd(st[i]-1,co(r,j-1,len[i]))]-op[pd(st[i]-1,co(l-1,j,len[i]))]+op[pd(st[i]-1,co(l-1,j-1,len[i]))]==0)
			{
				k++;
			}
			else break;
		}
		q[1][i]=k;
		k=0;
		for (int j=len[i];j>=1;j--)
		{
			int x1=pd(st[i]-1,co(r,j,len[i]));
			int x2=pd(st[i]-1,co(r,j-1,len[i]));
			int x3=pd(st[i]-1,co(l-1,j,len[i]));
			int x4=pd(st[i]-1,co(l-1,j-1,len[i]));
			if (op[pd(st[i]-1,co(r,j,len[i]))]-op[pd(st[i]-1,co(r,j-1,len[i]))]-op[pd(st[i]-1,co(l-1,j,len[i]))]+op[pd(st[i]-1,co(l-1,j-1,len[i]))]==0)
			{
				k++;
			}
			else break;
		}
		q[2][i]=k;
	}
	int zx=0;int cx=0;int zy=0;int cy=0;
	int w1=0;int w2=0;int w3=0;int w4=0;int op=0;
	for (int k=1;k<=s;k++)
	{
		if (q[1][k]==len[k]) op=op+q[1][k];
		else
		{
			if (q[1][k]>zx)
			{
				cx=zx;w2=w1;
				zx=q[1][k];
				w1=k;
			}
			else
			if (q[1][k]>cx)
			{
				cx=q[1][k];w2=k;
			}
							
			if (q[2][k]>zy)
			{
				cy=zy;w4=w3;
				zy=q[2][k];
				w3=k;
			}  
			else
			if (q[2][k]>cy)
			{
				cy=q[2][k];w4=k;
			}
		}
	}
	if (w3==w1)
	ans=max(ans,max((cx+zy+op)*(r-l+1),(zx+cy+op)*(r-l+1)));
	else
	ans=max(ans,(zx+zy+op)*(r-l+1));
	return ans;
	
}

int main()
{
//	freopen("data.in","r",stdin);
	freopen("puzzle.in","r",stdin);
	freopen("puzzle.out","w",stdout);
	scanf("%d",&T);
	while (T>0)
	{
		T--;
		scanf("%d%d",&s,&n);
		int m=0;
		
		int l=0;
		for (int i=1;i<=s;i++)
		{
			scanf("%d",&len[i]);
			st[i]=l;
			l=st[i];
			for (int j=1;j<=n;j++)
			{
				scanf("%s",ss+1);
				for (int k=1;k<=len[i];k++)
				{
					map[l]=ss[k]-48;
					l++;
				}
			}
			m=m+len[i];
		}
		int fj=sqrt(n*m);
		int ans=0;
		for (int i=1;i<=s;i++)
		{
			for (int j=1;j<=n;j++)
			{
				for (int k=1;k<=len[i];k++)
				{
					sum[co(j,k,len[i])]=sum[co(j-1,k,len[i])]+sum[co(j,k-1,len[i])]-sum[co(j-1,k-1,len[i])]+map[st[i]-1+co(j,k,len[i])];
					op[st[i]-1+co(j,k,len[i])]=sum[co(j,k,len[i])];
				}
			}
			if (n<=fj)
			{
				for (int j=1;j<=n;j++)
				{
					for (int k=j;k<=n;k++)
					{
						int r=0;
						for (int l=1;l<=len[i];l++)
						{
							if (sum[co(k,l,len[i])]-sum[co(j-1,l,len[i])]-sum[co(k,l-1,len[i])]+sum[co(j-1,l-1,len[i])]==0)
							{
								r++;
							}
							else
							{
								ans=max(ans,r*(k-j+1));
								r=0;
							}
						}
						ans=max(ans,r*(k-j+1));
					}
				}
			}
			else
			{
				for (int j=1;j<=len[i];j++)
				{
					for (int k=j;k<=len[i];k++)
					{
						int r=0;
						for (int l=1;l<=n;l++)
						{
							if (sum[co(l,k,len[i])]-sum[co(l,j-1,len[i])]-sum[co(l-1,k,len[i])]+sum[co(l-1,j-1,len[i])]==0)
							{
								r++;
							}
							else
							{
								ans=max(ans,r*(k-j+1));
								r=0;
							}
						}
						ans=max(ans,r*(k-j+1));
					}
				}
			}
		}
		if (n<=fj)
		{
			for (int i=1;i<=n;i++)
			{
				for (int j=i;j<=n;j++)
				{
					ans=max(ans,find(i,j));
				}
			}
		}
		else
		{
			for (int i=1;i<=s;i++)
			{
				for (int j=1;j<=n;j++)
				{
					int l=0;
					for (int k=1;k<=len[i];k++)
					{
						if (map[st[i]-1+co(j,k,len[i])]==1) break;
						l++;
					}
					d[1][j]=l;
					l=0;
					for (int k=len[i];k>=1;k--)
					{
						if (map[st[i]-1+co(j,k,len[i])]==1) break;
						l++;
					}
					d[2][j]=l;
					q[1][co(i,j,n)]=d[1][j];
					q[2][co(i,j,n)]=d[2][j];
				}
			}
			for (int k=1;k<=m;k++)
			{
				int r=0;
				for (int l=1;l<=n;l++)
				{
					while (r<=n && find(l,r+1)>=(r+1-l)*k) r++;
					ans=max(ans,(r+1-l)*k);
				}
			}
		}
		printf("%d\n",ans);
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值