您的位置:首页 > 其它

(POJ 1054)The Troublesome Frog <暴力枚举+剪枝 || DP>

2017-01-17 20:13 429 查看
题目链接:http://poj.org/problem?id=1054

题意:

给定一个行数和列数固定的网格,再给定若干个点,这些点是使用矩形上网格线交叉点的坐标来唯一确定的R行C列的网格,左上角的坐标为(1,1)右下角的坐标为(R,C),问给定的这些点能够组成最长可能路径是多长. 路径的定义的如下的:由若干连续且间隔相等的点组成,方向可任意,起始点和结束点均在网格之外的任意空间.也就是只要满足网格内连续两点之间的间距相等就可以了. 路径上要求至少三个点,而且起点和终点一定要在网格的外部.

分析:

枚举+剪枝

由于题目的数据的大小可以承受住O(n^2)的时间。所以我们可以先将所有点进行排序后,通过枚举每两个点作为一条路径的前两个点,然后求每种情况的值从中求出最大值。其中可以通过题意来进行明显的剪枝。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;

const int maxn = 5010;
short int g[maxn][maxn];
int R,C,N;

struct Node
{
int x,y;
}node[maxn];

int cmp(Node a,Node b)
{
if(a.x != b.x) return a.x < b.x;
else return a.y < b.y;
}

bool judge(int x,int y)
{
return x>0 && x<=R && y>0 && y<=C;
}

int deal()
{
int ans = 2,px,py,cx,cy,dx,dy,len;
for(int i=1;i<N;i++)
{
for(int j=i+1;j<=N;j++)
{
len = 2;
dx = node[j].x - node[i].x;//x之间的距离
dy = node[j].y - node[i].y;//y之间的距离
if(node[j].x + (ans - 2)*dx > R) break;//x方向上判断是否能达到当前ans的值,假设后面点全部存在
if(node[j].y + (ans - 2)*dy <=0 || node[j].y + (ans - 2)*dy > C) continue;//y方向上判断是否能达到当前ans的值,假设后面点全部存在
px = node[i].x - dx; py = node[i].y - dy;
if(judge(px,py)) continue;//起点是否在外面
cx = node[j].x; cy = node[j].y;
while(judge(cx + dx,cy + dy) && g[cx+dx][cy+dy])
{
len++;
cx += dx; cy += dy;
}
if(!judge(cx+dx,cy+dy))//终点是否在外面
{
ans = max(ans,len);
}
}
}
if(ans >= 3) return ans;
else return 0;

}

int main()
{
while(scanf("%d%d",&R,&C)!=EOF)
{
memset(g,0,sizeof(g));
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d%d",&node[i].x,&node[i].y);
g[node[i].x][node[i].y] = 1;
}
sort(node+1,node+N+1,cmp);
printf("%d\n",deal());
}
return 0;
}


DP

写出了上面枚举的代码后,我们可以从中得到启发,也可以用DP来解决。(DP只不过是高级的暴力而已)

动态规划解法中,dp[i][j]表示从j到i这条路径中最多经过多少个点. 那么有动态规划方程:

dp[i][j] = max(dp[j][k]) + 1, 其中k满足dist(i, j) = dist(j, k) 且 dir(i, j) = dir(j, k).

求出这些状态后,我们并不一定会取这些值,因为还要满足一个左端点和右端点都在外部.

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;

const int maxn = 5010;
short int g[maxn][maxn];//用来判断点是否被经过,并且去除点的编号
short int dp[maxn][maxn];
int R,C,n;

struct Node
{
int x,y;
}node[maxn];

int cmp(Node a,Node b)
{
if(a.x != b.x) return a.x < b.x;
else return a.y < b.y;
}

bool judge(int x,int y)
{
return x>0 && x<=R && y>0 && y<=C;
}

int DP()
{
int px,py,nx,ny,no;
short int ans = -1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
nx = 2*node[j].x - node[i].x;//i->j方向的下一个点:2*(node[j].x-node[i].x)+node[i].x
ny = 2*node[j].y - node[i].y;
if(judge(nx,ny))
{
no = g[nx][ny];
if(no != -1 && dp[j][no] != -1) dp[i][j] = dp[j][no] + 1;
else dp[i][j] = -1;//端点在网格内
}
else dp[i][j] = 2;
px = 2*node[i].x - node[j].x;//起点
py = 2*node[i].y - node[j].y;
if(!judge(px,py)) ans = max(ans,dp[i][j]);
}
}
if(ans >= 3) return ans;
else return 0;
}

int main()
{
while(scanf("%d%d",&R,&C)!=EOF)
{
memset(g,0xff,sizeof(g));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&node[i].x,&node[i].y);
sort(node+1,node+n+1,cmp);
for(int i=1;i<=n;i++)
g[node[i].x][node[i].y] = i;
printf("%d\n",DP());
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: