POJ1054 The Troublesome Frog
题意描述
给出一张 \(R\times C\) 网格图,其中 \(N\) 个节点上有标记。
求一条路径,使得路径上每个标记的间隔距离都相等的情况下,标记的数量最多,且至少为 \(3\) 个。
其中每条路径还需要保证向两端扩展一个间隔后将扩出网格图。
输出标记数量的最大值,如果得不出答案,输出 0
。
其中 \(1\leq R,C\leq 5000,3\leq N\leq 5000\)。
算法分析
枚举+剪枝。
首先将节点从左上到右下排序。
然后暴力枚举两两节点当做路径的起始节点和第二节点,并暴力向前扩展。
最坏时间复杂度 \(O(N^3)\),不难发现无法通过此题。
剪枝一:可行性剪枝
根据题目意思:每条路径还需要保证向两端扩展一个间隔后将扩出网格图。
我们可以在开始扩展前就判断是否能扩出图外。
在扩展结束后再判断一次是否能扩出图外。
其实这可以说是按照题意排除错误答案,不算做传统意义上的剪枝。
剪枝二:最优化剪枝
这是一个很强的剪枝。
我们假设当前得到的答案为 \(ans\)。
显然如果当前枚举的两个节点所构成的道路上的总结点数 \(\leq ans\),就可以直接剪枝。
具体判断方法看之后的代码。
时间复杂度
由于有了上面两个剪枝,特别是第二个剪枝,导致找到答案之后的所有枚举几乎全都被剪枝了。
所以时间复杂度趋向于 \(O(N^2)\),实测跑得飞快。
代码实现
实现复杂度较为简单,注意边界的判断。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 5010
#define INF 0x3f3f3f3f
using namespace std;
int r,c,n;
bool mp[N][N];
struct node{int x,y;}a[N];
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
bool cmp(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;}
int main(){
//freopen("frog.in","r",stdin);
//freopen("frog.out","w",stdout);
r=read(),c=read(),n=read();
for(int i=1;i<=n;i++){
a[i].x=read(),a[i].y=read();
mp[a[i].x][a[i].y]=true;
}
sort(a+1,a+n+1,cmp);
int ans=0;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++){
int dx=a[j].x-a[i].x;
int dy=a[j].y-a[i].y;
int prex=a[i].x-dx;
int prey=a[i].y-dy;
if(prex>=1 && prex<=r && prey>=1 && prey<=c) continue;
int nowx=a[j].x+ans*dx;
int nowy=a[j].y+ans*dy;
if(!(nowx>=1 && nowx<=r && nowy>=1 && nowy<=c)) continue;
int now=0;
nowx=a[j].x,nowy=a[j].y;
while(1){
nowx+=dx;nowy+=dy;
if(!(nowx>=1 && nowx<=r && nowy>=1 && nowy<=c)) break;
if(!mp[nowx][nowy]) break;
now++;
}
if(nowx>=1 && nowx<=r && nowy>=1 && nowy<=c) now=0;
ans=max(ans,now);
}
printf("%d\n",ans==0?0:ans+2);
return 0;
}