[JZOJ1320] 【Usaco2009 gold 】拯救奶牛

题目

在这里插入图片描述

题目大意

一个三角形的网格图,三角形与其有共同边的三角形相连。
起点到所有终点的最短距离。


思考历程

数据看起来还挺大的,所以不是什么图论算法。
这显然是一个结论题。
什么结论?
然后我就开始推:
一个点(x,y)(x,y),它可以到达(x,y1)(x,y+1)(x,y-1)(x,y+1)
如果yy是奇数,那么可以到达(x+1,y+1)(x+1,y+1),否则可以到达(x1,y1)(x-1,y-1)
将其在平面直角坐标系中表示出来,得到一个很好看的图(懒得画了)。
感性地想出了一个式子,感觉上好像没什么问题,打了交上去。
结果60分……


正解

这个方法有很多。

假设起点是上面那个(反正边是双向的),现在它要往下走:
如果它是正三角形,意味着它可以直接往下走。
如果它是倒三角形,就将其向左或向右一个,那就是正三角形的情况了。
可以推推式子,求出它走最短距离到达终点那一行的左右边界。
所谓走最短距离,就是先往下走,然后向左或向右,继续向下……
如果终点在边界内,答案就是最短距离。否则走到终点那一行的左边界或右边界,然后左右移动到终点。

还有一种特别好的方法。
我们如果只考虑上下走,那么对于那两个点来说,不管怎样,一定会走它们层数之差的距离。
这是我们以(1,1)(1,1)为端点的情况,那不妨考虑一下,将图旋转六十度,以(n,1)(n,1)(n,2n1)(n,2n-1)为顶点。
所以我们只需要算出它们分别以三个顶点的层数之差,加起来,就是答案。
比较好理解,所以我的程序打的就是这种方法。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
int main(){
	int n,m,sx,sy,sz;
	scanf("%d%d%d%d",&n,&m,&sx,&sy);
	sz=(sx*2-sy-1)/2+1,sy=(sy-1)/2+1;
	int ansx,ansy,ans=2147483647;
	for (int i=1;i<=m;++i){
		int x,y,z,t;
		scanf("%d%d",&x,&y);
		t=abs(x-sx)+abs((y-1)/2+1-sy)+abs((x*2-y-1)/2+1-sz);
		if (t<ans || t==ans && (x<ansx || x==ansx && y<ansy))
			ansx=x,ansy=y,ans=t;
	}
	printf("%d %d\n%d",ansx,ansy,ans+1);
	return 0;
}

总结

题目不要想得太复杂……
能用一个三角形来想的题目就不要傻傻地用平面直角坐标系来想……

posted @ 2019-03-04 12:25  jz_597  阅读(452)  评论(0编辑  收藏  举报