雷神领域 题解

162 阅读2分钟

题目链接

雷神领域
题目大意:在一个二维平面内给定n个值为1点,并按照要求补出其他点,然后从原点找一条路径,设该路径上包含1的行数为h,列数为l,求出min(h,l)的最大值。

思路

解题分两步。

1.补点

观察数据范围,暴力补点一定不可行。

一个简单的例子
红点代表已知合法点,绿点是要补的点。

设矩阵为M[N][N]M[i][j]表示矩阵的第i行第j列。

如果要查询点A(i,j)是否合法,设存在合法点P(t,j)Q(t,k)O(i,k),如果M[i][k]=1,则证明点A合法。

以上图为例:现在要查询点A(1,3)是否合法,找到这一列存在一个合法点P(3,3),在矩阵中找到一个和P(3,3)同一行的点也就是Q(3,1),因为Q(3,1)所在列中O(1,1)合法,所以证明A(1,3)合法。

那么如何进行快速的查询呢?
考虑把每个已知合法点的行与列并入一个集合
如果我们要查询点(i,j)是否合法,只需查看i,j是否处于同一集合中。因为如果二者在同一集合中,就说明通过已知合法点的合并,上文提到的PQO对应的行列坐标已经在同一集合之中,也就是说找到了一条P->Q->O的路径。如果不懂的话可以手推一下。

2.找到最优路径

简单的动态规划,设f[i][j]为第i行第j列时得到的答案。

如果M[i][j]=1,考虑f[i][j]f[i-1][j]f[i][j-1]f[i-1][j-1]转移而来,

  1. f[i-1][j]转移,占用的行数+1
  2. f[i][j-1]转移,占用的列数+1
  3. f[i-1][j-1]转移,二者都+1

综上由f[i-1][j-1]转移可以得到更优的解。

如果M[i][j]=0,那么就从f[i-1][j]f[i][j-1]中取最优。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
using namespace std;
const int N=5005;
int n,maxi,mayi,ans;
int fa[4*N],f[N][N];
inline int read()
{
	int ret=0,flag=1;char ch=getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')flag=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9')ret=ret*10+ch-'0',ch=getchar();
	return ret*flag;
}
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void merge(int x,int y)
{
	x=find(x);y=find(y);
	fa[x]=y;
}
void print()
{
	rep(i,1,maxi)
	{
		rep(j,1,mayi)
		{
			if(fa[i]==fa[j+10000])
			cout<<1<<" ";
			else cout<<0<<" ";
		}
		cout<<endl;
	}
}
int main()
{
	rep(i,1,20005)
	fa[i]=i;
	n=read();
	int x,y;
	rep(i,1,n)
	{
		x=read();y=read();
		maxi=max(maxi,x);
		mayi=max(mayi,y);
		merge(x,y+10000);		//开双倍并查集维护行与列 
	}
	print();
	for(int i=1;i<=20000;i++)find(i);
	rep(i,1,maxi)
	{
		rep(j,1,mayi)
		{
			if(fa[i]==fa[j+10000])
			f[i][j]=f[i-1][j-1]+1;
			else
			f[i][j]=max(f[i-1][j],f[i][j-1]);
			ans=max(ans,f[i][j]);
		}
	}
	cout<<ans<<endl;
}