一道莫名其妙的离散化dp

青蛙
(frog.pas/c/cpp)
【问题描述】
在韩国,成古利(一种青蛙)的顽皮是出名的,它总是在晚上到稻田上乱逛,并踩平水稻。
这种青蛙总是以直线跳过稻田,且每个跳跃的长度都是一样:
如下 Figure-1 是一片稻田,Figure-2 则是青蛙的路径,黑色的点表示被踩平的水稻
在晚上,有可能许多青蛙跳到你的稻田中,如 Figure-3 就是三只青蛙跳过的路径(注
意,有可能一个点被多只青蛙踩过) ,当然,你看到的并不是像 Figure-3 那样还有路径,而
是像 Figure-4 那样,只看到被踩平的点
或是往不同的方向跳跃:
不同青蛙可以有不同的跳跃长度:
从 Figure-4 你可以看出所有的路径,但你只对 3 个点以上的这样满足青蛙跳跃规则的路径
感兴趣。这样的路径又可能很多,现在你需要找出满足条件的最长路径(即踩过的点最多) 。
如 Figure-4 就是 7。
【输入格式】
第一行两个正整数 R 和 C,表示稻田行和列的数
第二行一个数 N,表示被踩平的点数
接下来有 N 行,每行两数 X,Y,表示被踩平点的坐标
【输出格式】
一个数,即最长的路径长度,如果没有满足条件,就输出 0
【输入样例】frog.in
6 7
18
1 1
6 2
3 5
1 5
4 7
1 2
1 4
1 6
1 7
2 1
2 3
2 6
4 2
4 4
4 5
5 4
5 5
6 6
【输出样例】frog.out

4



【数据范围】
数据 1 2 3 4 5 6 7 8 9 10
N 18 100 500 1000 2000 3000 3000 4000 4000 5000

Max(x,y) 7 20 55 1000 200 60 5000 1000 5000 3000


图片弄不上来几个意思?

随便扔张样例好了。。。

很显然一条蛙径上的点是在同一直线上,所以它们具有同一斜率(废话!)

那么如果(假设)能枚举所有斜率,然后再对点的距离处理下(呵呵)就能得出答案啦!

不过斜率是有无数种的,好一点的就是枚举平面任两点,然后再把这条直线扩出去(真难!)

不过注意到虽然最多有5000*5000个点,但是青蛙跳过的只有5000个点,所以枚举蛙点就行了!

既然确定斜率,还需知道一条线上有几个点,

首先将所有点按从左往右,从下到上排序,保证前面的点一定在后面的点左/下方

然后f[i][j] = (j,i)确定的斜率上1...i有多少点

设j关于i的对称点为k1,i关于j对称的点为k2

那么 f[i][j] = f[j][k] + 1(如果存在k1)

f[i][j] = 2(如果k1飞出去了)

f[i][j] = 0




注意!!!!!

自己实现的时候特别傻逼。。。

首先,一定先判断点是否出界,不然拿着出界的东西来搞会出事情!

然后,f[i][j] = 0并不好,假如你有三个点k,i,j满足蛙径定义,但你不一定保证k前面还有点

所以第三个方程应作f[i][j] = -INF

然后short站的内存是int的1/2(MLE?!)


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

const int maxn = 5e3 + 10;

struct P{
	int x,y;
	bool operator < (const P &b) const {
		if (y < b.y) return 1;
		if (y > b.y) return 0;
		return x > b.x;	
	}
}p[maxn];

short f[maxn][maxn],n,i,j,mx,my;
short s[maxn][maxn];

P dc(P a,P b) {return (P){a.x - (b.x - a.x),a.y - (b.y - a.y)};}

int main()
{
	#ifndef YZY
		freopen("frog.in","r",stdin);
		freopen("frog.out","w",stdout);
	#else
		freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> mx >> my >> n;
	memset(s,0,sizeof(s));
	for (i = 1; i <= n; i++) scanf("%d%d",&p[i].x,&p[i].y);
	sort (p + 1,p + n + 1);
	for (i = 1; i <= n; i++) s[p[i].x][p[i].y] = i;
	for (i = 1; i <= n; i++)
		for (j = 1; j < i; j++) {
			P k = dc(p[j],p[i]);
			if (k.x < 1 || k.y < 1 || k.x > mx || k.y > my) f[i][j] = 2;
			else if (s[k.x][k.y] > 0) f[i][j] = f[j][s[k.x][k.y]] + 1;
			else f[i][j] = -30000;
		}
	short ans = 0;
	for (i = 1; i <= n; i++)
	  	for (j = 1; j < i; j++) {
			P k = dc(p[i],p[j]);
			if ((k.x > mx || k.y > my || k.x < 1 || k.y < 1) && f[i][j] >= 3) ans = max(ans,f[i][j]);
	  	}
	cout << ans;
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值