7-1 布线问题
印刷电路板将布线区域划分成 n×m 个方格阵列,要求确定连接方格阵列中的方格a 点到方格b 的最短布线方案。在布线时,电路只能沿直线布线,为了避免线路相交,已布了线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。问线路至少穿过几个方格。下图是一个布线的例子:
   输入格式:
第1行输入三个正整数,表示布线区域的尺寸n和m(2≤n,m≤100),以及被封锁的方格数k;接下来k行,每行给出两个整数x和y(1≤x≤n,1≤y≤m),表示被封锁方格的坐标(横坐标在前纵坐标在后,下同),坐标原点在左上角,并设定左上角方格坐标为(1, 1),方格间的间距为1;最后一行输入4个整数,分别为方格a与方格b的坐标。
输出格式:
输出只有1行1个整数,为方格a到方格b的最短布线距离,若无法布线,输出-1。
输入样例:
7 7 14
1 3
1 4
2 4
3 5
4 4
4 5
5 1
5 5
6 1
6 2
6 3
7 1
7 2
7 3
3 2 4 6
 输出样例:
10
 提示:本题不使用分支限界法,不得分!
思路:
经典BFS/DFS走迷宫问题,注意题目样例 初始方格也算一个1个间距 因此初始化距离时为1即可
AC代码:
/*
* @Author: Spare Lin
* @Project: AcWing2022
* @Date: 2023/1/8 22:43
* @Description: 7-1 布线问题
* @URL: https://pintia.cn/problem-sets/1471841287149830144/exam/problems/1471861519453036544
*/
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 1e2 + 7;
const int dx[4] = {0, 1, 0, -1};
const int dy[4] = {-1, 0, 1, 0};
typedef pair<int, int> PII;
int n, m, k, ax, ay, bx, by;
int g[N][N], dist[N][N];
bool st[N][N];
int bfs() {
    queue<PII> q;
    q.push({ax, ay});
    memset(dist, -1, sizeof dist);
    dist[ax][ay] = 1; //注意题意 初始化为1
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 1 || a > n || b < 1 || b > m) continue;
            if (st[a][b]) continue;
            dist[a][b] = dist[t.x][t.y] + 1;
            st[a][b] = true;
            q.push({a, b});
        }
    }
    return dist[bx][by];
}
signed main() {
    cin >> n >> m >> k;
    while (k--) {
        int x, y;
        cin >> x >> y;
        st[x][y] = true;
    }
    cin >> ax >> ay >> bx >> by;
    cout << bfs() << endl;
    return 0;
}
 7-2 最少翻译
据美国动物分类学家内斯特·迈尔推算,世界上有超过100万种动物,各种动物都有自己的语言,假设动物A可以与动物B进行通信,但它不能与动物C通信,动物C只能与动物B通信,所以动物A、B之间的通信需要动物B来当翻译,问两个动物之间相互通信至少需要多少个翻译。
输入格式:
测试数据中第一行包含两个整数n(2<=n<=1000)、m(1<=m<=50000),其中n代表动物的数量,动物编号从0开始,n个动物编号为0~n-1,m表示可以相互通信动物数,接下来的m行中包含两个数字分别代表两种动物可以相互通信,在接下来包含一个整数k(k <= 20),代表查询的数量,对应每个查询包含两个数字,表示彼此通信的两个动物。
输出格式:
对于每个查询,输出这两个动物彼此通信至少需要多少个翻译,若它们之间无法通过翻译来通信,则输出-1。
输入样例:
3 2
0 1
1 2
2
0 0
0 2
 输出样例:
0
1
 提示:本题不使用分支限界法,不得分!
思路:
问题转化为求一个无向图中两点间的最短路径,关于最短路径算法我们可以用dijkstra、floyd、spfa、bellman-ford等等,没了解过的同学可以去了解一下,这里我写了两种方法,以下是AC代码
AC代码:
/*
* @Author: Spare Lin
* @Project: AcWing2022
* @Date: 2023/1/8 22:43
* @Description: 7-2 最少翻译
* @URL: https://pintia.cn/problem-sets/1471841287149830144/exam/problems/1471863969160429568
*/
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 1e3 + 7, M = 5e4 + 7, inf = 0x3f3f3f3f;
typedef pair<int, int> PII;
int n, m, k;
bool st[N], dist[N];
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
//给定一个无向图 求两点之间是否到达 若能到达则输出最短距离 否则输出-1
//堆优化dijkstra 用spfa类似与bfs
int dijkstra(int start, int end) {
    if (start == end) return 0;
    memset(dist, 0x3f, sizeof dist);
    dist[start] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.emplace(0, start); // dist-sno
    while (!heap.empty()) {
        auto t = heap.top();
        heap.pop();
        int ver = t.y, distance = t.x;
        if (st[ver]) continue;
        st[ver] = true;
        for (int i = h[ver]; ~i; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[ver] + 1) {
                dist[j] = dist[ver] + 1;
                heap.emplace(dist[j], j);
            }
        }
    }
    if (dist[end] == inf) return -1;
    return dist[end];
}
//spfa写法
int bfs(int start, int end) {
    queue<int> q;
    memset(dist, 0x3f, sizeof dist);
    dist[start] = 0;
    q.push(start);
    st[start] = true;
    while (!q.empty()) {
        auto t = q.front(); q.pop();
        st[t] = false;
        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[t] + 1) {
                dist[j] = dist[t] + 1;
                if (!st[j]) {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    if (dist[end] == inf) return -1;
    return dist[end];
}
signed main() {
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        add(x, y), add(y, x);// 建无向图
    }
    cin >> k;
    while (k--) {
        int start, end;
        cin >> start >> end;
        cout << bfs(start, end) << endl;
        //cout << dijkstra(start, end) << endl;
    }
    return 0;
}
 7-3 拯救安妮
原始森林中有很多危险动物,第1种是金刚(一种体型庞大的猩猩),金刚是一种危险的动物,如果遇到金刚,必死无疑;第2种是野狗,它不会像金刚那么危险,但它会咬人,被野狗咬过两次(不管是否同一只野狗咬的),也会死!
安妮是一个美国的女孩,她不幸迷失于原始森林中。杰克非常担心她,他要到原始森林找她。杰克能否找到一条安全的路径营救出安妮呢?
输入格式:
输入的第1行是单个数字t(0<=t<=20),表示测试用例的数目。
每个测试用例是一个原始森林地图,第一行的整数n(0<n<=30),表示原始森林的行数和列数,接下来n行,每行n个字符,每个字符代表的含义如下:
p表示杰克;a表示安妮;r表示道路;k表示金刚;d表示野狗。
请注意,杰克只能在上、下、左、右4个方向移动。
输出格式:
对于每个测试用例,如果杰克能够安全救出安妮,则在一行中输出“Yes”,否则在一行中输出“No”。
输入样例:
4
3
pkk
rrd
rda
3
prr
kkk
rra
4
prrr
rrrr
rrrr
arrr
5
prrrr
ddddd
ddddd
rrrrr
rrrra
 输出样例:
Yes
No
Yes
No
 提示:本题不使用分支限界法,不得分!
思路:
同样是BFS走迷宫问题,不同的是注意附带的被野狗咬的次数不能超过2次,我们用一个dist数组记录走到每个位置时被野狗咬的次数即可,然后在扩展时我们遇到野狗特判即可
AC代码:
/*
* @Author: Spare Lin
* @Project: AcWing2022
* @Date: 2023/1/8 22:43
* @Description: 7-3 拯救安妮
* @URL: https://pintia.cn/problem-sets/1471841287149830144/exam/problems/1471863969160429568
*/
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 35;
const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
typedef pair<int, int> PII;
int t, n, sx, sy, ex, ey;
bool st[N][N];
char g[N][N];
int dist[N][N]; //记录被咬次数
bool bfs() {
    queue<PII> q;
    q.emplace(sx, sy);
    st[sx][sy] = true;
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        if (t.x == ex && t.y == ey) return true;
        for (int i = 0; i < 4; i++) {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 1 || a > n || b < 1 || b > n) continue;
            if (g[a][b] == 'k' || st[a][b]) continue;
            if (g[a][b] == 'd' && dist[t.x][t.y] == 1) continue;
            //此时已经不能再被狗咬
            st[a][b] = true;
            if (g[a][b] == 'd') dist[a][b] = dist[t.x][t.y] + 1;
            q.emplace(a, b);
        }
    }
    return false;
}
signed main() {
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                cin >> g[i][j];
                if (g[i][j] == 'p') sx = i, sy = j;
                if (g[i][j] == 'a') ex = i, ey = j;
            }
        }
        memset(st, 0, sizeof st);
        memset(dist, 0, sizeof dist);
        bfs() ? puts("Yes") : puts("No");
    }
    return 0;
}
 7-4 饥饿的章鱼 (小数据版本)
作者 J.Liau单位 泉州师范学院
小易总是感觉饥饿,所以作为章鱼的小易经常出去寻找贝壳吃。最开始小易在一个初始位置x_0。对于小易所处的当前位置x,他只能通过神秘的力量移动到 4 * x + 3或者8 * x + 7。因为使用神秘力量要耗费太多体力,所以它只能使用神秘力量最多20次。贝壳总生长在能被17整除的位置(比如:位置0、17、34、51、……)。小易需要你帮忙计算最少需要使用多少次神秘力量就能吃到贝壳。
输入格式:
输入有多组测试用例,每组用例输入一个初始位置x0,范围在1到1,000,000,000。
输出格式:
对每组用例输出小易最少需要使用神秘力量的次数,如果使用次数使用完还没找到贝壳,则输出-1。
输入样例:
100
200
 输出样例:
2
-1
 提示:
如果不采用分支限界法,本题不得分!
思路:
BFS迭代 两种情况 开一个哈希表来记录访问的点 用来减少迭代次数
AC代码:
#include <bits/stdc++.h>
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int mod = 1e9 + 7, inf = 1e9 + 7;
int x;
int bfs(int x) {
    if (x < 1 || x >= inf) return -1;
    x %= mod;
    queue<PII> q;
    unordered_map<int, int> vis;//哈希表记录是否被访问过 减少迭代次数
    q.emplace(x, 0); // num - cnt
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        if (t.x % 17 == 0) return t.y;
        if (t.y < 20) {
            auto cur1 = t, cur2 = t;
            cur1.x = (4 * t.x + 3) % mod, cur1.y = t.y + 1;
            cur2.x = (8 * t.x + 7) % mod, cur2.y = t.y + 1;
            if (!vis.count(cur1.x)) {
                vis[cur1.x] = 1;
                q.emplace(cur1);
            }
            if (!vis.count(cur2.x)) {
                vis[cur2.x] = 1;
                q.emplace(cur2);
            }
        }
    }
    return -1;
}
signed main() {
    while (cin >> x) cout << bfs(x) << endl;
    return 0;
}
 原题来源:网易2017内推笔试编程题-饥饿的小易
注:本题是老师把原题数据改小后发布的,原题数据量很大,BFS迭代若不开哈希表会爆内存。
原题AC代码:
#include <bits/stdc++.h>
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int mod = 1e9 + 7, inf = 1e9 + 7;
int n, x;
int bfs(int x) {
    if (x < 1 || x >= inf) return -1;
    x %= mod;
    queue<PII> q;
    unordered_map<int, int> vis;
    q.emplace(x, 0); // num - cnt
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        if (t.x % mod == 0) return t.y;
        if (t.y < 1e5) {
            auto cur1 = t, cur2 = t;
            cur1.x = (4 * t.x + 3) % mod, cur1.y = t.y + 1;
            cur2.x = (8 * t.x + 7) % mod, cur2.y = t.y + 1;
            if (!vis.count(cur1.x)) {
                vis[cur1.x] = 1;
                q.emplace(cur1);
            }
            if (!vis.count(cur2.x)) {
                vis[cur2.x] = 1;
                q.emplace(cur2);
            }
        }
    }
    return -1;
}
signed main() {
    while (cin >> x) {
        cout << bfs(x) << endl;
    }
    return 0;
}
 7-5 机器的重量
设某一机器由n个部件组成,部件编号为1~n,每一种部件都可以从m个不同的供应商处购得,供应商编号为1~m。设wij是从供应商j处购得的部件i的重量,cij是相应的价格。对于给定的机器部件重量和机器部件价格,计算总价格不超过d的最小重量机器设计。
输入格式:
第1行输入3个正整数n,m和d(1≤n、m≤20,1≤d≤100)。接下来n行输入wij(每行m个整数,且1≤wij≤10),最后n行输入cij(每行m个整数,且1≤cij≤50)。
输出格式:
输出的第1行包括n个整数,表示每个部件(按1~n的顺序)在对应编号的供应商处购得,第2行为对应的最小重量。
输入样例:
3 3 7
1 2 3
3 2 1
2 3 2
1 2 3
5 4 2
2 1 2
 输出样例:
1 3 1
4
 提示:
如果不采用分支限界法,本题不得分!
思路:
dfs深搜,回溯
AC代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e2 + 7;
int n, m, d, res = 0x3f3f3f3f, curw, curc;
int w[N][N], c[N][N], ans[N], sale[N];
void dfs(int u) {
    if (u > n) { //搜索结束
        res = curw; //更新最小重量
        for (int j = 1; j <= n; j++) ans[j] = sale[j];
    } else {
        for (int j = 1; j <= m; j++) {
            if (curc + c[u][j] <= d && curw + w[u][j] < res) {
                sale[u] = j;
                curc += c[u][j], curw += w[u][j];
                dfs(u + 1);
                curc -= c[u][j], curw -= w[u][j]; //回溯
            }
        }
    }
}
signed main() {
    cin >> n >> m >> d;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> w[i][j];//wij是从供应商j处购得的部件i的重量
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> c[i][j]; //cij是相应的价格
        }
    }
    dfs(1);
    for (int i = 1; i <= n; i++) {
        cout << ans[i] << ' ';
    }
    cout << endl;
    cout << res << endl;
    return 0;
}
 7-2 马走日
作者 J.Liau单位 泉州师范学院
在m行n列的中国象棋棋盘上,马在左下角处,马走日字,且假设只能往右走(右上或右下),求从起点到右上角处有几种不同的走法、最快走法的步数以及方案。注意,m行n列的棋盘,若左下角坐标是(1, 1),则右上角坐标是(n, m)。
输入格式:
输入有多个用例,每个用例包括1行,各有两个整数m, n(1≤m, n≤20),以输入m=0、n=0结束。
输出格式:
对输入中的每组测试数据,输出2行,第1行1个数字表示从起点(1, 1)到右上角(n, m)处总共不同的走法数;第2行第1个数字输出最快走法的步数;接下来输出最快走法的方案(输出格式见样例),如果存在多个答案输出第1个满足条件结果(为确保答案的唯一性,请按下图的顺序扩展点)。如无法到达终点,只需要输出-1。
   输入样例:
4 4
6 4
0 0
 输出样例:
2
2: [1,1]->[3,2]->[4,4]
-1
 提示:
如果不采用分支限界法,本题不得分!
思路:dfs求方案数 ,bfs求最短路径,开一个二维PII存扩展到每个点的前驱用来打印路径,注意题目右上角为(m,n)与输入是相反的,这题找bug找了几个小时~
AC代码:
(没法测了不知道能不能AC)
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 30, dx[4] = {1, 2, 2, 1}, dy[4] = {-2, -1, 1, 2}; //按照图中顺序扩展
const int inf = 0x3f3f3f3f;
int n, m, res;
int dist[N][N];
bool st[N][N], vis[N][N];
PII pre[N][N];
void dfs(int x, int y) { //求方案数
    vis[x][y] = true;
    if (x == n && y == m) {
        res++;
        return;
    }
    for (int i = 0; i < 4; i++) {
        int a = x + dx[i], b = y + dy[i];
        if (a < 1 || a > n || b < 1 || b > m) continue;
        vis[x][y] = true;
        dfs(a, b);
        vis[x][y] = false;
    }
}
void bfs() { //迭代求最快的走法
    queue<PII> q;
    q.emplace(1, 1);
    memset(st, 0, sizeof st);
    memset(dist, 0x3f, sizeof dist);
    memset(pre, -1, sizeof pre);
    st[1][1] = true;
    dist[1][1] = 0;
    pre[1][1] = {m, n};
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 1 || a > n || b < 1 || b > m) continue;
            if (st[a][b]) continue;
            if (pre[a][b].x != -1) continue;
            st[a][b] = true;
            dist[a][b] = dist[t.x][t.y] + 1;
            pre[a][b] = t;
            q.emplace(a, b);
        }
    }
}
string get(int x, int y) {
    return "[" + to_string(x) + "," + to_string(y) + "]";
}
vector<string> ans;
void getRoad() {
    PII end = {m, n};
    while (true) {
        ans.push_back(get(end.x, end.y));
        if (end.x == 1 && end.y == 1) break;
        end = pre[end.x][end.y];
    }
}
signed main() {
    while (cin >> n >> m && n , m) {
        bfs();
        if (dist[m][n] == inf) {
            cout << -1 << endl;
        } else {
            res = 0;
            memset(vis, 0, sizeof vis);
            dfs(1, 1);
            cout << res << endl; //方案数
            getRoad();
            cout << dist[m][n] << ": "; //最短路径
            for (int i = ans.size() - 1; i >= 0; i--) {
                if (i != 0) cout << ans[i] << "->";
                else cout << ans[i];
            }
            ans.clear();
            cout << endl;
        }
    }
    return 0;
}






![[ 数据结构 ] 集合覆盖问题(贪心算法)](https://img-blog.csdnimg.cn/img_convert/e6eadf11a0e3cb54c04ae053481b9ad3.png)










![[ 数据结构 ] 背包问题(动态规划)](https://img-blog.csdnimg.cn/img_convert/535b7a89319ab66170ef73a8a0894aef.png)

