题目链接
雷神领域
题目大意:在一个二维平面内给定个值为
点,并按照要求补出其他点,然后从原点找一条路径,设该路径上包含
的行数为
,列数为
,求出
的最大值。
思路
解题分两步。
1.补点
观察数据范围,暴力补点一定不可行。

设矩阵为。
表示矩阵的第
行第
列。
如果要查询点是否合法,设存在合法点
,
,
,如果
,则证明点
合法。
以上图为例:现在要查询点是否合法,找到这一列存在一个合法点
,在矩阵中找到一个和
同一行的点也就是
,因为
所在列中
合法,所以证明
合法。
那么如何进行快速的查询呢?
考虑把每个已知合法点的行与列并入一个集合。
如果我们要查询点是否合法,只需查看
是否处于同一集合中。因为如果二者在同一集合中,就说明通过已知合法点的合并,上文提到的
、
、
对应的行列坐标已经在同一集合之中,也就是说找到了一条
的路径。如果不懂的话可以手推一下。
2.找到最优路径
简单的动态规划,设为第
行第
列时得到的答案。
如果,考虑
由
、
和
转移而来,
- 由
转移,占用的行数
;
- 由
转移,占用的列数
;
- 由
转移,二者都
;
综上由转移可以得到更优的解。
如果,那么就从
、
中取最优。
代码
#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;
}