【百度之星资格赛】 下棋(构图)

NM 的棋盘上有一个受伤的国王与一个要去救援国王的骑士,他们每个单位时间必须同时移动一次寻找对方。如下图所示,黑色的图例表示国王(右)或骑士(左)当前所在的位置,那么灰色的位置表示在一次移动中他们可能到达的位置。国王伤势严重,因此他必须在K个单位时间内得到骑士的救援,否则会挂掉。问国王是否可以在K个单位时间内获得救援,如果可以,最短需要花多少个单位时间。

Input

第一行包含一个整数 T,1T50 代表测试数据的组数,接下来 T 组测试数据。

每组测试数据第一行包含三个整数 N,M,K , 且 2N,M1000 1K200 。第二行两个整数 Xking,Yking ,对应国王所在方格的坐标。第三行两个整数 Xknight,Yknight ,对应骑士所在方格的坐标。其中 1Xking,XknightN,1Yking,YknightM ,保证骑士与国王一开始不在同一个方格内且他们都可以移动。:

Output

对于每组测试数据,输出两行:

第一行输出:"Case #i:"。 i 代表第 i 组测试数据。

第二行输出测试数据的结果,如果国王可以得到救援,则输出最快需要花多少个单位时间。否则,输出“OH,NO!”。

Sample Input
2
3 2 1
1 1
3 1
3 3 1
1 1
1 2 
Sample Output
Case #1:
1
Case #2:
OH,NO!

把国王和骑士的位置都转换成相对位置。国王必须在骑士的右下方(计算方便)

10*10的表打出来是这样的(红色表示骑士位置,其他点的数字表示骑士走到这里需要花几步)

4 1 2 1 4 3 2 3 4 5 
1 2 3 2 1 2 3 4 3 4 
2 3 2 3 2 3 2 3 4 5 
1 2 3 2 1 2 3 4 3 4 
4 1 2 1 4 3 2 3 4 5 
3 2 3 2 3 2 3 4 3 4 
2 3 2 3 2 3 4 3 4 5 
3 4 3 4 3 4 3 4 5 4 
4 3 4 3 4 3 4 5 4 5 
5 4 5 4 5 4 5 4 5 6

遍历一个范围(怎么找下面会说)内的所有点,如果国王到这个点的最少步数等于这个点上的值,那么它就是我们要找的答案之一了。我们要找答案里面的最小值。

那么怎么找这个范围?这里思考麻烦了点。首先计算出它们的相对距离dx,dy。但是题目不是还给了一个原本格子的范围n和m吗,这个范围是有用的。如果国王在骑士的右边,就取骑士右边的范围。反过来一样考虑。

这题还有个陷阱:不要把骑士和国王的走法弄反了。

#include<iostream>  
#include<algorithm>  
#include<string>  
#include<map>  
#include<vector>  
#include<cmath>  
#include<queue>  
#include<string.h>  
#include<stdlib.h>  
#include<stdio.h>
#define ll long long  
using namespace std;
ll v[1010][1010];
struct node{
    int a,b;
    ll c;
};
int x,y;
void bfs(){
    node p;
    p.a=2;
    p.b=2;
    p.c=0;
    queue<node> q; 
    q.push(p);
    while(!q.empty()){
        node fr=q.front();
        node w;
        q.pop();
        if(fr.a-1>=0&&fr.b-2>=0&&v[fr.a-1][fr.b-2]==-1){
            v[fr.a-1][fr.b-2]=fr.c+1;
            w.a=fr.a-1;w.b=fr.b-2;w.c=fr.c+1;
            q.push(w);
        }
        if(fr.a-2>=0&&fr.b-1>=0&&v[fr.a-2][fr.b-1]==-1){
            v[fr.a-2][fr.b-1]=fr.c+1;
            w.a=fr.a-2;w.b=fr.b-1;w.c=fr.c+1;
            q.push(w);
        }
        if(fr.a+1<=1005&&fr.b+2<=1005&&v[fr.a+1][fr.b+2]==-1){
            v[fr.a+1][fr.b+2]=fr.c+1;
            w.a=fr.a+1;w.b=fr.b+2;w.c=fr.c+1;
            q.push(w);
        }
        if(fr.a+2<=1005&&fr.b+1<=1005&&v[fr.a+2][fr.b+1]==-1){
            v[fr.a+2][fr.b+1]=fr.c+1;
            w.a=fr.a+2;w.b=fr.b+1;w.c=fr.c+1;
            q.push(w);
        }
        if(fr.a-1>=0&&fr.b+2<=1005&&v[fr.a-1][fr.b+2]==-1){
            v[fr.a-1][fr.b+2]=fr.c+1;
            w.a=fr.a-1;w.b=fr.b+2;w.c=fr.c+1;
            q.push(w);
        }
        if(fr.a-2>=0&&fr.b+1<=1005&&v[fr.a-2][fr.b+1]==-1){
            v[fr.a-2][fr.b+1]=fr.c+1;
            w.a=fr.a-2;w.b=fr.b+1;w.c=fr.c+1;
            q.push(w);
        }
        if(fr.a+1<=1005&&fr.b-2>=0&&v[fr.a+1][fr.b-2]==-1){
            v[fr.a+1][fr.b-2]=fr.c+1;
            w.a=fr.a+1;w.b=fr.b-2;w.c=fr.c+1;
            q.push(w);
        }
        if(fr.a+2<=1005&&fr.b-1>=0&&v[fr.a+2][fr.b-1]==-1){
            v[fr.a+2][fr.b-1]=fr.c+1;
            w.a=fr.a+2;w.b=fr.b-1;w.c=fr.c+1;
            q.push(w);
        }
    }
}
int main(){
    memset(v,-1,sizeof(v));
    v[2][2]=2;
    bfs();
    int t;
    cin>>t;
    int cnt=0;
    int n,m,k;
    while(t--){
        printf("Case #%d:\n",++cnt);
        cin>>n>>m>>k;
        int x1,y1,x2,y2,row,col;
        cin>>x1>>y1>>x2>>y2;
        x=abs(x1-x2)+2;y=abs(y1-y2)+2;
        if(x1>=x2)
            row=n-x2+2;
        else
            row=x2-1+2;
        if(y1>=y2)
            col=m-y2+2;
        else
            col=y2-1+2;
        ll s=100000000010;
        for(int i=2;i<=min(1004,min(x+2,row));++i){
            for(int j=2;j<=min(1004,min(y+2,col));++j){
                if(v[i][j]==max(abs(x-i),abs(y-j)))
                    s=min(s,v[i][j]);
            }
        }
        if(s<=k)
            cout<<s<<endl;
        else
            cout<<"OH,NO!"<<endl;
    }
    return 0;
} 
直接分析的方法(bfs最短路):

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <assert.h>
#define clr(a,b)    memset(a,b,sizeof(a))
using namespace std;

typedef long long LL;

int T;
int n, m, k;

int g[1010][1010];

int dx[8] = {-1,-2,-2,-1,1,2,2,1};
int dy[8] = {-2,-1,1,2,-2,-1,1,2};

int x,y,xx,yy;
queue<pair<int,int> > q;

int main()
{
    scanf("%d",&T);
    int cas = 1;
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);

        scanf("%d%d",&x,&y);
        scanf("%d%d",&xx,&yy);

        int ans = max(abs(x-xx), abs(y-yy) );
        if(ans % 2 == 1)    ans++;

        printf("Case #%d:\n", cas++);

        clr(g, -1);
        while(!q.empty())    q.pop();

        g[xx][yy] = 0;
        q.push(make_pair(xx, yy));
        while(!q.empty())
        {
            pair<int,int> h = q.front();
            q.pop();
            int curx = h.first;
            int cury = h.second;

            if(g[curx][cury] > k)
                continue;

            for(int i=0; i<8; i++)
            {
                int nx = curx + dx[i];
                int ny = cury + dy[i];

                if(nx < 1 || nx > n || ny < 1 || ny > m || g[nx][ny] != -1)
                    continue;

                g[nx][ny] = g[curx][cury] + 1;
                q.push(make_pair(nx, ny));

                int t1 = g[nx][ny];
                int t2 = max(abs(nx - x), abs(ny - y));

                int t;

                if(t2 == 0)  //这个点跟国王重合
                {
                    if(t1 == 0)    t = 2;  //国王和骑士都走出一步再回到原点
                    else if(t1 == 1)    t = 3; //国王走三步回到原点,骑士走出一步再回到原点再走出一步
                    else    t = t1;
                }
                else if(t1 >= t2)    t = t1;  //骑士的步数比国王多
                else
                {
                    if( (t2 - t1) % 2 == 0 ) t = t2;
                    else    t = t2 + 1;  //最后国王的步数多余了一步
                }
                ans = min(ans, t);
            }
        }

        if(ans > k)
            puts("OH,NO!");
        else
            printf("%d\n", ans);
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值