罗勇军 →《算法竞赛·快冲300题》每日一题:“超级骑士” ← DFS

【题目来源】
http://oj.ecustacm.cn/problem.php?id=1810
http://oj.ecustacm.cn/viewnews.php?id=1023

https://www.acwing.com/problem/content/3887/

【题目描述】
现在在一个无限大的平面上,给你一个超级骑士。
超级骑士有N种走法,请问这个超级骑士能否到达平面上的所有点。
每种走法输入两个数字 xx 和 yy,表示超级骑士可以从任意一点 (x,y) 走到 (x+xx,y+yy)。

【输入格式】
输入第一行为正整数 T,表示存在 T 组测试数据。(1≤T≤100)
对于每组测试数据,第一行输入正整数 N,表示有 N 种走法。(1≤N≤100)
接下来N行,每行两个正整数 xx 和 yy。(
-100≤xx,yy≤100

【输出格式】
对于每组测试数据,如果可以到达平面上所有点,输出Yes,否则输出No。

【输入样例】
2
3
1 0
0 1
-2 -1
5
3 4
-3 -6
2 -2
5 6
-1 4

【输出样例】
Yes
No

【算法分析】
因为题意会走回头路,所以不能用动态规划算法,需要用 BFS 或 DFS 算法。由于题意规定每步走法由输入的两个数字 xx 和 yy 决定,表示超级骑士可以从
任意一点 (x,y) 走到 (x+xx,y+yy),且 -100≤xx, yy≤100。不失一般性,可将起点设为 (100,100)。若设 (xx,yy) 为 (-100,-100)、(-100,100)、(100,-100)、(100,100),那么走一步最远可到 (100-100,100-100)、(100-100,100+100)、(100+100,100-100)、(100+100,100+100),即 (0, 0)、(0, 200)、(200, 0)、(200, 200)。
虽然题目问能不能到达所有的点,但其实不用真的检查是否能到所有的点。只需检查从某个任意点 (sx, sy) 出发,存在 (sx, sy) 的上、下、左、右的点都可到达,可立即返回“Yes”,不用再遍历其他的点,这就是
剪枝的应用。这是因为,若给定的走法能够保证存在某个任意点 (sx, sy) 的上、下、左、右的点都可到达,那么平面上的任意点都可达。或从直观上分析,能否走到平面上的所有点,取决于骑士走法的粒度(或称步幅),粒度越小越有可能达到所有点。如输入样例中的 (1,0)、(0,1)、(-2,-1),因为粒度较小,平面上的所有点都可达。而输入样例中的 (3,4)、(-3,-6)、(2,-2)、(5,6)、(-1,4),因为粒度较大,平面上有的点就走不到。
DFS算法模板:https://blog.csdn.net/hnjzsyjyj/article/details/125801217

void dfs(int step){
    判断边界{
        输出解 
    }
 
    尝试每一种可能{
        满足check条件{
            标记
            继续下一步:dfs(step+1)
            恢复初始状态(回溯的时候要用到)
        }
    }
}

BFS算法模板:https://blog.csdn.net/hnjzsyjyj/article/details/118736059

助记:建-入-量:头-出-入”。
 
其中,“建-入-量:头-出-入”各字的解析如下:
建:建队
入:入队
量:队中元素个数。作为while循环的条件。
头:队头
出:出队
入:入队

一个记忆场景,“小猫咪在好的洞口,想洞。先用胡子过洞口大小后,然后用头出入洞”。

【算法代码】

#include<bits/stdc++.h>
using namespace std;

const int maxn=105;
int dx[maxn],dy[maxn];
bool f[2*maxn][2*maxn];
int sx=100,sy=100;
int n;

bool dfs(int x, int y) {
    f[x][y]=true;
    if(f[sx-1][sy] && f[sx+1][sy] && f[sx][sy-1] && f[sx][sy+1]) { //prune
        return true;
    }
    for(int i=1; i<=n; i++) {
        int nx=x+dx[i];
        int ny=y+dy[i];
        if(nx<0 || nx>200 || ny<0 || ny>200) continue; //check boundary
        if(f[nx][ny]) continue; //having gone
        if(dfs(nx,ny)) return true;
    }
    return false;
}

int main() {
    int T;
    cin>>T;
    while(T--) {
        cin>>n;
        for(int i=1; i<=n; i++) {
            cin>>dx[i]>>dy[i];
        }
        memset(f,0,sizeof(f));
        if(dfs(sx,sy)) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}


/*
in:
2
3
1 0
0 1
-2 -1
5
3 4
-3 -6
2 -2
5 6
-1 4

out:
Yes
No
*/




【参考文献】
https://blog.csdn.net/weixin_43914593/article/details/131791202







 

猜你喜欢

转载自blog.csdn.net/hnjzsyjyj/article/details/132405334