POJ 1054 The Troublesome Frog(dp+剪枝)

题意:好多青蛙祸害庄稼,已知一个 R ∗ C R*C RC的稻田。给出被青蛙踩到的庄稼的坐标,已知一只青蛙的跳跃距离是一定的,它从稻田外踩过稻田中的 M M M个点然后又跳出稻田,求某只青蛙踩出的 M M M最大值。(也就是说一只青蛙可以踩出一条线来。)小于三的情况直接输出 0 0 0

先按 x x x排序,如果 x x x相等按 y y y排序,然后枚举两个点确定一条直线(这两个点在直线上是相邻的),然后找这条直线上共有多少个这样的点(两相邻点的距离相同。)

已知枚举出 i , j i,j ij点( i i i为起点);

d x = v [ j ] . x − v [ i ] . x ; dx = v[j].x - v[i].x; dx=v[j].xv[i].x;

d y = v [ j ] . y − v [ i ] . y ; dy = v[j].y - v[i].y; dy=v[j].yv[i].y;

m a ma ma为之前求出的最优值(这里体现出dp的思想)

剪枝:

1、如果 v [ i ] . x + m a ∗ d x > R v[i].x + ma*dx > R v[i].x+madx>R,则跳出这次对 j j j的枚举;因为我们的 x x x是从小到大排序的,剩下的 x x x肯定比 v [ i ] . x v[i].x v[i].x大,连当前最小的 x x x都不能产生连续的超过 m a ma ma的点,那么之后的肯定也不可以。

2、如果 ( v [ i ] . x − d x , v [ i ] . y − d y ) (v[i].x - dx, v[i].y - dy) (v[i].xdx,v[i].ydy)在棋盘内,剪枝;因为题目要求了从圈外开始跳跃的。

3、如果 ( v [ i ] . x + m a ∗ d x , v [ i ] . y + m a ∗ d y ) (v[i].x + ma*dx, v[i].y + ma*dy) (v[i].x+madx,v[i].y+mady)不再棋盘内,剪枝;此时显然不能有连续的超过 m a ma ma个点。

需要注意的是,

  • 在统计 v [ i ] , v [ j ] v[i],v[j] v[i],v[j]所形成的路径上的点数的时候,点必须连续,即路径上的每个点都必须存在,否则就不能形成一条路
while (sx >= 1 && sx <= n && sy >= 1 && sy <= m) {
	if (!exi[sx][sy]) { cnt = 0; break; }//不连续,不是一条路径
	cnt++;
	sx += dx, sy += dy;
}
#define ll long long
#define inf 0x3f3f3f3f
#define vec vector<int>
#define P pair<int,int>
#define MAX	5005

struct p {
	int x, y;
}a[MAX];
bool cmp(p p1, p p2) {
	if (p1.x == p2.x)return p1.y < p2.y;
	else return p1.x < p2.x;
}
int n, m, N, exi[MAX][MAX];
int main() {
	while (scanf("%d %d %d", &n, &m, &N) != EOF) {
		memset(exi, 0, sizeof(exi));
		for (int i = 0; i < N; i++)
			scanf("%d %d", &a[i].x, &a[i].y), exi[a[i].x][a[i].y] = 1;
		sort(a, a + N, cmp);
		int ma = 0;
		for (int i = 0; i < N; i++) {
			for (int j = i + 1; j < N; j++) {
				int dx = a[j].x - a[i].x, dy = a[j].y - a[i].y;
				int tx = a[i].x + ma * dx, ty = a[i].y + ma * dy;
				int sx = a[i].x - dx, sy = a[i].y - dy;
				if (tx > n)break;//再之后的x都比a[j].x大,因此不可能有更好的结果
				if (tx<1 || tx>n || ty<1 || ty>m)continue;//终点出界了
				if (sx >= 1 && sx <= n && sy >= 1 && sy <= m)continue;//起点必须在图外
				sx += dx, sy += dy; int cnt = 0;
				while (sx >= 1 && sx <= n && sy >= 1 && sy <= m) {
					if (!exi[sx][sy]) { cnt = 0; break; }//不连续,不是一条路径
					cnt++;
					sx += dx, sy += dy;
				}
				if (cnt > ma)ma = cnt;//必须不少于3个
			}
		}
		if (ma >= 3)cout << ma << endl;
		else cout << 0 << endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值