• 2018 Wannafly summer camp Day3--Knight


    Knight
    题目描述:
    有一张无限大的棋盘,你要将马从((0,0))移到((n,m))
    每一步中,如果马在((x,y)(x,y)),你可以将它移动到 ((x+1,y+2)(x+1,y+2)),
    ((x+1,y-2)(x+1,y−2)),((x-1,y+2)(x−1,y+2)),((x-1,y-2)(x−1,y−2)),
    ((x+2,y+1)(x+2,y+1)),((x+2,y-1(x+2,y−1)),((x-2,y+1)(x−2,y+1)或(x-2,y-1)(x−2,y−1))
    你需要最小化移动步数。
    输入:
    第一行一个整数tt表示数据组数 ((1leq tleq 1000))

    每组数据一行两个整数(n,m (|n|,|m| leq 10^9))

    输出:
    每组数据输出一行一个整数表示最小步数。
    样例输入
    2
    0 4
    4 2
    样例输出
    2
    2

    • 由于数据有(10^9),所以BFS被毙了(~ ̄▽ ̄)~,没想到什么好的做法,所以BFS打表找规律= ̄ω ̄=。

    • 打表结果及代码

    #include<iostream>
    #include <queue>
    using namespace std;
    int dir[8][2] = {
    	{1,2},{1,-2},{-1,2},{-1,-2},
    	{2,1},{2,-1},{-2,1},{-2,-1}
    };
    int n, m;
    int maze[1100][1100];
    bool vis[1100][1100];
    struct Point {
    	int x, y, step;
    	Point(int _x, int _y, int _step) :
    		x(_x), y(_y), step(_step) {}
    };
    void bfs(int sx, int sy)
    {
    	queue<Point>q;
    	q.push(Point(sx, sy, 0));
    	vis[sx][sy] = 1;
    	maze[sx][sy] = 0;
    	while (!q.empty())
    	{
    		int x = q.front().x;
    		int y = q.front().y;
    		int step = q.front().step;
    		maze[x][y] = step;
    		q.pop();
    		for (int i = 0; i < 8; i++)
    		{
    			int tx = x + dir[i][0];
    			int ty = y + dir[i][1];
    			if (!vis[tx][ty]&&tx<61&&ty<61&&tx>=0&&ty>=0)
    			{
    				q.push(Point(tx, ty, step + 1));
    				vis[tx][ty] = 1;
    			}
    		}
    	}
    }
    int main() {
    	//freopen("1.txt", "w", stdout);
    	bfs(30, 30);
    	for (int i = 0; i < 60; i++) {
    		for (int j = 0; j <60; j++) {
    			cout << maze[i][j] << " ";
    		}
    		cout << endl;
    	}
    	return 0;
    }
    
    • 从上面看,很明显是有规律的,据说大佬能一眼就看出来,以前我是不信的,直到现场有dalao花了4分钟拿了一血……<@_@>蒟蒻只能慢慢推了。首先先把上面的数据放到Excel里面,先预处理一下,将每个答案作为点,以起点为原点建立平面直角坐标系,结果如下:

    之前我犯了一个错误,BFS起点放到数组边界上去了,应该放到偏中心的位置,把表打出来。将答案统一起来看,从2开始,所有相同的答案围成了一个八边形,这个八边形与坐标轴平行的边都是4层,不平行的都是3层,同时答案基本是向外递增的这样看的时候会发现两个特殊的地方,一个是((0,1),(1,0),(-1,0),(0,-1))这四个点为3,((2,0),(0,2),(0,-2),(-2,0))着四个点4,所以将这些点加入特判。
    不难看出,这个表关于坐标轴对称(图中蓝色线),同时也关于(y=+-x)对称(图中橙色线),所以(x)轴正半轴为起点,逆时针划分为8个区域,每个区域都一样,只需要考虑1号区域就行了。
    现在考虑的为1号区域,希望找到递增的答案之间存在的关系,这个关系为(y=x/2),可以发现这条直线上的整点正好是答案的递增(0,0)->(2,1)->(4,2).....->(x,floor(x/2)))。将这条直线画出来。(floor()是对一个数值向下取整)
    现在看(y=x/2)下方的点,满足关系(y<x/2),也就是(y<x-y)(精度问题,计算时应该用double),而且下方的点都是在刚才所说的八边形的4层边上,所以可以发现将这些点作如下变换后可以将横坐标和(y=x/2)对应:

    double(x-y-y)/4.0*2;
    

    最后将上面这个值取反(+x-y)就是答案。同理可以推出(y=x/2)上方的点,满足关系(y>x/2),在刚才所说的八边形的3层边上,最后推出

    double(x-y-y)/3.0*2;
    
    • Code
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include<algorithm>
    #include<iostream>
    #include<queue>
    using namespace std;
    typedef long long ll;
    ll fun(ll x, ll y) {
    
    	if (x == 1 && y == 0) {
    		return 3;
    	}
    	if (x == 2 && y == 2) {
    		return 4;
    	}
    	ll delta = x - y;
    	if (y>delta) {
    		return delta - 2 * floor(((double)(delta-y)) / 3.0);
    	}
    	else {
    		return delta - 2 * floor(((double)(delta-y)) / 4.0);
    	}
    }
    
    int main()
    {
    
    	int t;
    	cin >> t;
    	while (t--)
    	{
    		ll x, y;
    		cin >> x >> y;
    		x = abs(x);
    		y = abs(y);
    		if (x < y) {
    			swap(x, y);
    		}
    		cout << fun(x, y) << endl;
    	}
    
    	return 0;
    }
    
    • 最后,为正经题解
      Knight:
      不妨假设(x>=y>=0)
      (x<=2y) 时,定义每一步的冗余值(w_i=3-dx-dy),那么(Σw_i=Σ(2-dx)=3*步数-x-y),显然我们只需要最小化冗余值。我们先只用(+2,+1)(若x 为奇数则加一步(+1,+2))走到(x,y’),然后通过将(+2,+1)替换为2 个(+1,+2)使得(0<=y-y’<3)
      (y-y’=0),则冗余值为0,显然最小。
      (y-y’=1),则将(+1,+2)替换为(+2,+1)和(-1,+2)或将2 个(+2,+1)替换为(+1,+2),(+1,+2),(+2,-1),冗余值为2,显然最小。(此处需要特判(2,2))
      (y-y’=2),则加上((+2,+1)和(-2,+1)),冗余值为4,由于不存在冗余值为1的步,所以最小。
      (x>2y) 时,定义每一步的冗余值(w_i=2-dx),那么(Σw_i=Σ(2-dx)=2*步数-x),显然我们只需要最小化冗余值。我们先只使用(+2,+1)走到(2y,y),然后用
      (+2,+1)和(+2,-1)走到((x’,y)使得0<=x-x’<4)
      (x-x’=0)则冗余值为0,显然最小。
      (x-x’=1) 则将之前的(+2,+1)改为(+1,+2)和(+2,-1),冗余值为1,显然最
      小。(此处需要特判(1,0))若(x-x’=2) 则加上(+1,+2)和(+1,-2),冗余值为2,由x/2+y 的奇偶性可知
      最小。
      (x-x’=3) 则加上(+2,+1),(+2,+1),(-1,-2),冗余值为3,由x/2+y 的奇偶性可知最小。
      时间复杂度O(t)
  • 相关阅读:
    软件的竞争力:性能 CQ
    2010.7.11 OA项目组一周工作报告 CQ
    2010.8.22 OA项目组一周工作报告 CQ
    2010.7.18 OA 项目组一周工作报告 CQ
    我的音乐 CQ
    2010.7.25 OA项目组一周工作报告 CQ
    JavaScript中的关于this
    递归函数的应用
    es6中的对象的可计算的属性名
    undefined 和 undeclared 的区别
  • 原文地址:https://www.cnblogs.com/FlyerBird/p/9431685.html
Copyright © 2020-2023  润新知