【九十七】【算法分析与设计】图论,迷宫,1207. 大臣的旅费,走出迷宫,石油采集,after与迷宫,逃离迷宫,3205. 最优配餐,路径之谜

1207. 大臣的旅费 - AcWing题库

很久以前,TT 王国空前繁荣。

为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,TT 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。

同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

JJ 是 TT 国重要大臣,他巡查于各大城市之间,体察民情。

所以,从一个城市马不停蹄地到另一个城市成了 JJ 最常做的事情。

他有一个钱袋,用于存放往来城市间的路费。

聪明的 JJ 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。

具体来说,一段连续的旅途里,第 11 千米的花费为 1111,第 22 千米的花费为 1212,第 33 千米的花费为 1313,…,第 xx 千米的花费为 x+10x+10。

也就是说,如果一段旅途的总长度为 11 千米,则刚好需要花费 1111,如果一段旅途的总长度为 22 千米,则第 11 千米花费 1111,第 22 千米花费 1212,一共需要花费 11+12=2311+12=23。

JJ 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式

输入的第一行包含一个整数 nn,表示包括首都在内的 TT 王国的城市数。

城市从 11 开始依次编号,11 号城市为首都。

接下来 n−1n−1 行,描述 TT 国的高速路(TT 国的高速路一定是 n−1n−1 条)。

每行三个整数 Pi,Qi,DiPi,Qi,Di,表示城市 PiPi 和城市 QiQi 之间有一条双向高速路,长度为 DiDi 千米。

输出格式

输出一个整数,表示大臣 JJ 最多花费的路费是多少。

数据范围

1≤n≤1051≤n≤105,

1≤Pi,Qi≤n1≤Pi,Qi≤n,

1≤Di≤10001≤Di≤1000

输入样例:

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

输出样例:

135

1.

树的直径,树中两点之间的距离的最大值是多少?求这个值的做法是,先随便找一个点,然后找到距离该点最远距离的点A,再找到距离A点最远距离的点B,AB两点的距离就是最大直径.

2.

用dfs可以找到A点最远点B和对应的距离.

dfs遍历每一个点,对于每一个点都具有一个信息,距离A点的距离是多少,作为节点信息.

3.

用邻接表存储图的信息.

用一个vector<vector<p>> g;类型表示邻接表,外层vector下标表示每一个点,每一个点对应的vector存储pair<int,int>类型,表示从下标点可以走到的点和距离.

 
#include<bits/stdc++.h>
using namespace std;
#define int long long // 定义 int 为 long long 类型
#define endl '\n' // 定义换行符

using p=pair<int,int>; // 定义 pair<int, int> 类型的别名为 p

int n; // 城市数量
vector<vector<p>> g; // 邻接表表示的图

int ret; // 存储最长路径长度
int lastpos; // 存储最长路径的终点
vector<bool>visited; // 访问标记数组

// 深度优先搜索函数
void dfs(int i,int path){
    if(path>ret)lastpos=i; // 如果当前路径长度大于已知最长路径,更新终点位置
    ret=max(ret,path); // 更新最长路径长度
    
    visited[i]=true; // 标记当前节点已访问
    for(auto&x:g[i]){ // 遍历当前节点的邻接节点
        if(!(visited[x.first]))dfs(x.first,path+x.second); // 如果邻接节点未访问,继续递归搜索
    }
    
    visited[i]=false; // 回溯,标记当前节点未访问
}

// 初始化函数
void init(){
    ret=0,lastpos=0; // 初始化最长路径长度和终点位置
    visited.assign(n+1,false); // 初始化访问标记数组

}

// 求解函数
void solve(){
    dfs(1,0); // 从节点 1 开始第一次深度优先搜索
    dfs(lastpos,0); // 从第一次搜索到的终点开始第二次深度优先搜索
    cout << (ret * ret + 21 * ret) / 2; // 输出结果,计算最大花费
}

// 主函数
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); // 加速输入输出
    cin>>n; // 输入城市数量
    g.assign(n+1,vector<p>()); // 初始化邻接表
    for(int i=1;i<=n-1;i++){
        int start,end,length;
        cin>>start>>end>>length; // 输入每条边的信息
        g[start].push_back({end,length}); // 添加边到邻接表
        g[end].push_back({start,length}); // 添加边到邻接表(双向)
    }
    init(); // 调用初始化函数
    solve(); // 调用求解函数
}

走出迷宫

链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网

题目描述

小明现在在玩一个游戏,游戏来到了教学关卡,迷宫是一个N*M的矩阵。

小明的起点在地图中用“S”来表示,终点用“E”来表示,障碍物用“#”来表示,空地用“.”来表示。

障碍物不能通过。小明如果现在在点(x,y)处,那么下一步只能走到相邻的四个格子中的某一个:(x+1,y),(x-1,y),(x,y+1),(x,y-1);

小明想要知道,现在他能否从起点走到终点。

输入描述:

本题包含多组数据。

每组数据先输入两个数字N,M

接下来N行,每行M个字符,表示地图的状态。

数据范围:

2<=N,M<=500

保证有一个起点S,同时保证有一个终点E.

输出描述:

每组数据输出一行,如果小明能够从起点走到终点,那么输出Yes,否则输出No

示例1

输入

复制3 3 S.. ..E ... 3 3 S## ### ##E

3 3

S..

..E

...

3 3

S##

##E

输出

复制Yes No

Yes

No

1.

迷宫从某一个点出发,寻找是否存在一条路径到达另一个指定点,只需要用dfs遍历所有的可以走的位置即可.

用一个visited存储可以走的路径和不可以走的路径,走过的路径是不可以走的位置.

2.

找某点到另一个点是否有路径,不需要回溯操作,回溯操作有点时候会导致时间复杂度变大.

这道题回溯会导致时间超时.

 
#include<bits/stdc++.h>
using namespace std;
#define int long long // 定义 int 为 long long 类型
#define endl '\n' // 定义换行符

using p = pair<int, int>; // 定义 pair<int, int> 类型的别名为 p
p start, end1; // 定义起点和终点

int n, m; // 定义迷宫的行数和列数
vector<vector<char>> g; // 定义迷宫的矩阵

vector<p> d = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 定义四个移动方向:右、下、左、上
vector<vector<bool>> visited; // 定义访问标记矩阵

// 深度优先搜索函数,判断是否能从起点到达终点
bool dfs(int i, int j) {
    if (i == end1.first && j == end1.second) { // 如果当前点是终点,返回 true
        return true;
    }

    visited[i][j] = true; // 标记当前点已访问
    for (auto& dd : d) { // 遍历四个方向
        int x = i + dd.first;
        int y = j + dd.second;
        if (x >= 0 && x < n && y >= 0 && y < m && !visited[x][y]) { // 判断新位置是否在边界内且未访问
            if (dfs(x, y)) return true; // 递归搜索,如果找到路径返回 true
        }
    }
    // visited[i][j] = false; // 不需要回溯
    return false; // 如果所有方向都无法到达终点,返回 false
}

// 初始化函数
void init() {
    visited.assign(n, vector<bool>(m, false)); // 初始化访问标记矩阵
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (g[i][j] == '#') visited[i][j] = true; // 将障碍物标记为已访问
        }
    }
}

// 求解函数
void solve() {
    if (dfs(start.first, start.second)) { // 从起点开始深度优先搜索
        cout << "Yes" << endl; // 如果可以到达终点,输出 "Yes"
    } else {
        cout << "No" << endl; // 否则输出 "No"
    }
}

// 主函数
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
    while (cin >> n >> m) { // 输入迷宫的行数和列数
        g.assign(n, vector<char>(m)); // 初始化迷宫矩阵
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cin >> g[i][j]; // 输入迷宫的每个字符
                if (g[i][j] == 'S') {
                    start = {i, j}; // 找到起点位置
                }
                if (g[i][j] == 'E') {
                    end1 = {i, j}; // 找到终点位置
                }
            }
        }
        init(); // 调用初始化函数
        solve(); // 调用求解函数
    }
}

石油采集

链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网

题目描述

随着海上运输石油泄漏的问题,一个新的有利可图的行业正在诞生,那就是撇油行业。如今,在墨西哥湾漂浮的大量石油,吸引了许多商人的目光。这些商人们有一种特殊的飞机,可以一瓢略过整个海面20米乘10米这么大的长方形。(上下相邻或者左右相邻的格子,不能斜着来)当然,这要求一瓢撇过去的全部是油,如果一瓢里面有油有水的话,那就毫无意义了,资源完全无法利用。现在,商人想要知道,在这片区域中,他可以最多得到多少瓢油。

地图是一个N×N的网络,每个格子表示10m×10m的正方形区域,每个区域都被标示上了是油还是水

输入描述:

测试输入包含多条测试数据

测试数据的第一行给出了测试数据的数目T(T<75)

每个测试样例都用数字N(N<50)来表示地图区域的大小,接下来N行,每行都有N个字符,其中符号’.’表示海面、符号’#’表示油面。

输出描述:

输出格式如下“Case X: M”(X从1开始),M是商人可以最多得到的油量。

示例1

输入

复制1 6 ...... .##... ...... .#..#. .#..## ......

1

6

......

.##...

......

.#..#.

.#..##

......

输出

复制Case 1: 3

Case 1: 3

1.

找网格中具有多少个1x2的矩形,计算横纵坐标累加值的奇数和偶数的个数,具有的矩形个数是min(odd,even).

odd是奇数,even是偶数.

 
#include<bits/stdc++.h>
using namespace std;
#define int long long // 定义 int 为 long long 类型
#define endl '\n' // 定义换行符
using p=pair<int,int>; // 定义 pair<int, int> 类型的别名为 p

vector<p>d={{0,1},{1,0},{0,-1},{-1,0}}; // 定义四个移动方向:右、下、左、上

int t, n; // 测试数据的数量和地图区域的大小
vector<vector<char>> g; // 定义地图矩阵

int ret; // 记录最多可以撇油的数量
int even, odd; // 记录偶数和奇数格子的数量
int count1=1; // 记录测试用例编号

// 深度优先搜索函数,用于遍历油区域
void dfs(int i, int j) {
    if((i+j)&1) odd++; // 如果 (i+j) 是奇数,odd++
    else even++; // 否则 even++

    if(g[i][j]=='#') g[i][j]='.'; // 将当前油格子标记为已访问

    for(auto& dd : d) { // 遍历四个方向
        int x = i + dd.first;
        int y = j + dd.second;
        if(x >= 0 && x < n && y >= 0 && y < n && g[x][y] == '#') { // 判断新位置是否在边界内且是油
            dfs(x, y); // 递归搜索
        }
    }
}

// 初始化函数
void init() {
    ret = 0, even = 0, odd = 0; // 初始化变量
}

// 求解函数
void solve() {
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            even = 0, odd = 0; // 每次搜索前重置计数器
            if(g[i][j] == '#') dfs(i, j); // 如果当前格子是油,开始深度优先搜索
            ret += min(even, odd); // 取偶数和奇数中的最小值累加到结果中
        }
    }
    cout << "Case " << count1++ << ": " << ret << endl; // 输出结果
}

// 主函数
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
    
    cin >> t; // 输入测试数据的数量
    
    while(t--) { // 处理每组测试数据
        cin >> n; // 输入地图区域的大小
        g.assign(n, vector<char>(n)); // 初始化地图矩阵
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                cin >> g[i][j]; // 输入地图的每个字符
            }
        }
        init(); // 调用初始化函数
        solve(); // 调用求解函数
    }
}

after与迷宫

链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网

题目描述

after的算法书的遗落在一个叫做AIJ的迷宫中了,这个迷宫有N*M个房间,迷宫的入口为(1,1),算法书遗落在(r,c)。迷宫中的房间有四种状态:空房间、无法进入的房间、有墨菲斯托存在的房间和有莉莉丝存在的房间。墨菲斯托会否定一切,而莉莉丝会诱惑人做一种叫做YK的活动。after是一个意志薄弱的人,他遇到了墨菲斯托和莉莉丝之后,便会变成眼神空洞的超级YK机器人。after每步可以从他当前的房间走至上下左右四个房间的其中一个房间。after害怕变成超级YK机器人,所以要尽快拿到算法书然后从入口逃离。问after最少需要走多少步才可以在不变成超级YK机器人的情况下从入口出发取回算法书并逃离迷宫?

输入描述:

第一行一个正整数T(T<=10),表示共有T组数据。

对于每组数据,第一行四个正整数N,M,r,c(1<=N,M<=1000;1<=r<=N;1<=c<=M)。

接下来N行,每行M个字符,每个表示房间的状态,“.”表示空房间,“*”表示无法进入的房间,“F”表示有墨菲斯托存在的房间,“M”表示有莉莉丝存在的房间。

数据保证(1,1)为“.”。

输出描述:

对每组数据输出一行,即after最少需要走的步数。若after无法取回算法书,则输出“IMPOSSIBLE”(不带引号)。

示例1

输入

复制1 4 4 4 3 ..** *F.. *.*. *M.F

1

4 4 4 3

..**

*F..

..

*M.F

输出

复制14

14

1.

不能够同时遇到F和M,所以有两种情况,允许遇到F或者允许遇到M.找最短的距离,从出发点前往魔法书的位置然后还需要返回到出发点,距离乘以2即可.

2.

bfs维护存储出发点到所有位置的最短距离.

 
#include<bits/stdc++.h>
using namespace std;
#define int long long // 定义 int 为 long long 类型
#define endl '\n' // 定义换行符
using p = pair<int, int>; // 定义 pair<int, int> 类型的别名为 p

vector<p> d = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 定义四个移动方向:右、下、左、上

int t; // 测试数据的数量
int n, m, r, c; // 地图的行数、列数,以及算法书的位置

vector<vector<char>> g; // 定义地图矩阵
vector<vector<int>> dis1; // 定义从起点到各位置的最短距离矩阵(允许遇到 F)
vector<vector<int>> dis2; // 定义从起点到各位置的最短距离矩阵(允许遇到 M)
int ret; // 记录最终结果

// 广度优先搜索函数,用于计算最短路径
void bfs(vector<vector<int>>& dis, char ch) {
    queue<p> q; // 定义队列用于 BFS
    q.push({0, 0}); // 从起点开始
    dis[0][0] = 0; // 起点到自己的距离为 0
    
    while (!q.empty()) {
        auto top = q.front(); // 获取队首元素
        q.pop(); // 弹出队首元素
        int i = top.first, j = top.second; // 当前坐标
        if (i == r && j == c) return; // 如果到达目标位置,返回
        for (auto& dd : d) { // 遍历四个方向
            int x = i + dd.first;
            int y = j + dd.second;
            
            // 判断新位置是否在边界内,且不是障碍物、指定角色,且未被访问
            if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] != '*' && g[x][y] != ch && dis[x][y] == LLONG_MAX) {
                dis[x][y] = dis[i][j] + 1; // 更新新位置的距离
                q.push({x, y}); // 将新位置加入队列
            }
        }
    }
}

// 初始化函数
void init() {
    ret = 0; // 重置结果
    dis1.assign(n, vector<int>(m, LLONG_MAX)); // 初始化距离矩阵,设为无穷大
    dis2.assign(n, vector<int>(m, LLONG_MAX)); // 初始化距离矩阵,设为无穷大
}

// 求解函数
void solve() {
    bfs(dis1, 'F'); // 计算不遇到 F 的最短路径
    bfs(dis2, 'M'); // 计算不遇到 M 的最短路径
    ret = min(dis1[r][c], dis2[r][c]); // 取两种情况中的最短距离
    if (ret != LLONG_MAX)
        cout << 2 * ret << endl; // 输出最短路径长度的两倍
    else cout << "IMPOSSIBLE" << endl; // 如果无法到达,输出 "IMPOSSIBLE"
}

// 主函数
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
    
    cin >> t; // 输入测试数据的数量
    while (t--) { // 处理每组测试数据
        cin >> n >> m >> r >> c; // 输入地图的行数、列数,以及算法书的位置
        r--, c--; // 将算法书位置调整为从 0 开始的索引
        g.assign(n, vector<char>(m)); // 初始化地图矩阵
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cin >> g[i][j]; // 输入地图的每个字符
            }
        }
        init(); // 调用初始化函数
        solve(); // 调用求解函数
    }
}

逃离迷宫

链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网

题目描述

给你一个n*m的图,地图上'.'代表可以走的地方,而'#'代表陷阱不能走,

'P'代表人物位置,'K'代表钥匙,'E'代表出口。人物一个,钥匙有多个,

('K'的数量<=50)),出口一个,每个位置可以向(上,下,左,右)四个

方向走一格,花费一个单位时间,现在你需要花费最少的时间拿到钥匙

然后从迷宫的出口出去(若没有钥匙,则不能进入迷宫出口所在的格子)。

输入描述:

第一行一个整数T(T <= 50),代表数据的组数

接下来一行n,m(n<=500,m<=500),代表地图的行和列

接下来n行,每行一个长度为m的字符串,组成一个图。

输出描述:

如果可以出去,输出所花费的最少时间。

如果不能出去,输出一行"No solution"。

示例1

输入

复制3 5 5 ....P ##..E K#... ##... ..... 5 5 P.... ..... ..E.. ..... ....K 5 5 P#..E .#.#. .#.#. .#.#. ...#K

3

5 5

....P

##..E

K#...

##...

.....

5 5

P....

.....

..E..

.....

....K

5 5

P#..E

.#.#.

.#.#.

.#.#.

...#K

输出

复制No solution 12 No solution

No solution

12

No solution

1.

入口,钥匙点,出口,我们需要从入口到达其中一个钥匙点,然后去出口,求最短的距离.

存储入口到所有位置的最短距离,和出口到所有位置的最短距离,遍历每一个钥匙点,如果距离存在,找最短就可以了.

 
#include<bits/stdc++.h>
using namespace std;
#define int long long // 定义 int 为 long long 类型
#define endl '\n' // 定义换行符
using p = pair<int, int>; // 定义 pair<int, int> 类型的别名为 p

vector<p> d = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 定义四个移动方向:右、下、左、上

int t, n, m; // t 为测试数据的数量,n 为地图的行数,m 为地图的列数
vector<vector<char>> g; // 定义地图矩阵

vector<vector<int>> dis1; // 定义从起点到各位置的最短距离矩阵
vector<vector<int>> dis2; // 定义从出口到各位置的最短距离矩阵
p start, end1; // 定义起点和终点
int ret; // 记录最终结果
vector<p> yaoshi; // 定义钥匙的位置列表

// 广度优先搜索函数,用于计算最短路径
void bfs(vector<vector<int>>& dis, p point) {
    dis[point.first][point.second] = 0; // 起点距离为 0
    queue<p> q; // 定义队列用于 BFS
    q.push(point); // 将起点加入队列
    while (!q.empty()) {
        auto top = q.front(); // 获取队首元素
        q.pop(); // 弹出队首元素
        int i = top.first; // 当前坐标
        int j = top.second; // 当前坐标
        if (point == start && i == end1.first && j == end1.second) continue; // 如果是起点且到达终点,继续
        for (auto& dd : d) { // 遍历四个方向
            int x = i + dd.first; // 新位置的行坐标
            int y = j + dd.second; // 新位置的列坐标
            // 判断新位置是否在边界内,且不是陷阱,且未被访问
            if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] != '#' && dis[x][y] == LLONG_MAX) {
                q.push({x, y}); // 将新位置加入队列
                dis[x][y] = dis[i][j] + 1; // 更新新位置的距离
            }
        }
    }
}

// 初始化函数
void init() {
    dis1.assign(n, vector<int>(m, LLONG_MAX)); // 初始化距离矩阵,设为无穷大
    dis2.assign(n, vector<int>(m, LLONG_MAX)); // 初始化距离矩阵,设为无穷大
    ret = LLONG_MAX; // 初始化结果为无穷大
}

// 求解函数
void solve() {
    bfs(dis1, start); // 计算从起点到各位置的最短路径
    bfs(dis2, end1); // 计算从出口到各位置的最短路径
    
    for (auto& x : yaoshi) { // 遍历所有钥匙位置
        int i = x.first; // 钥匙的行坐标
        int j = x.second; // 钥匙的列坐标
        
        // 判断起点到钥匙和出口到钥匙的距离是否存在
        if (dis1[i][j] != LLONG_MAX && dis2[i][j] != LLONG_MAX) {
            ret = min(ret, dis1[i][j] + dis2[i][j]); // 更新最短路径
        }
    }
    if (ret != LLONG_MAX) cout << ret << endl; // 输出最短路径
    else cout << "No solution" << endl; // 如果无法到达,输出 "No solution"
}

// 主函数
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出
    
    cin >> t; // 输入测试数据的数量
    while (t--) {        
        cin >> n >> m; // 输入地图的行数和列数
        g.assign(n, vector<char>(m)); // 初始化地图矩阵
        yaoshi.clear(); // 清空钥匙列表
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cin >> g[i][j]; // 输入地图的每个字符
                if (g[i][j] == 'P') { // 如果是人物位置
                    start.first = i;
                    start.second = j;
                }
                if (g[i][j] == 'E') { // 如果是出口位置
                    end1.first = i;
                    end1.second = j;
                }
                if (g[i][j] == 'K') { // 如果是钥匙位置
                    yaoshi.push_back({i, j});
                }
            }
        }
        
        init(); // 调用初始化函数
        solve(); // 调用求解函数
    }
    return 0;
}

3205. 最优配餐 - AcWing 题库

栋栋最近开了一家餐饮连锁店,提供外卖服务。

随着连锁店越来越多,怎么合理的给客户送餐成为了一个急需解决的问题。

栋栋的连锁店所在的区域可以看成是一个 n×nn×n 的方格图(如下图所示),方格的格点上的位置上可能包含栋栋的分店(绿色标注)或者客户(蓝色标注),有一些格点是不能经过的(红色标注)。

方格图中的线表示可以行走的道路,相邻两个格点的距离为 11。

栋栋要送餐必须走可以行走的道路,而且不能经过红色标注的点。

送餐的主要成本体现在路上所花的时间,每一份餐每走一个单位的距离需要花费 11 块钱。

每个客户的需求都可以由栋栋的任意分店配送,每个分店没有配送总量的限制。

现在你得到了栋栋的客户的需求,请问在最优的送餐方式下,送这些餐需要花费多大的成本。

输入格式

输入的第一行包含四个整数 n,m,k,dn,m,k,d,分别表示方格图的大小、栋栋的分店数量、客户的数量,以及不能经过的点的数量。

接下来 mm 行,每行两个整数 xi,yixi,yi,表示栋栋的一个分店在方格图中的横坐标和纵坐标。

接下来 kk 行,每行三个整数 xi,yi,cixi,yi,ci,分别表示每个客户在方格图中的横坐标、纵坐标和订餐的量。(注意,可能有多个客户在方格图中的同一个位置)

接下来 dd 行,每行两个整数,分别表示每个不能经过的点的横坐标和纵坐标。

输出格式

输出一个整数,表示最优送餐方式下所需要花费的成本。

数据范围

前 30%30% 的评测用例满足:1≤n≤201≤n≤20。

前 60%60% 的评测用例满足:1≤n≤1001≤n≤100。

所有评测用例都满足:1≤n≤1000,1≤m,k,d≤n2,1≤xi,yi≤n1≤n≤1000,1≤m,k,d≤n2,1≤xi,yi≤n。

可能有多个客户在同一个格点上。

每个客户的订餐量不超过 10001000,每个客户所需要的餐都能被送到。

输入样例:

10 2 3 3 1 1 8 8 1 5 1 2 3 3 6 7 2 1 2 2 2 6 8

输出样例:

29

1.

多源最短路径,我们要求客户点距离多个商家位置的最短距离,例如客户点A距离商家B,C最短距离.

将B,C点依次加入到队列中,然后bfs维护数据距离.

2.

用dis存储每一个点到达某一个商家点的最短距离,遍历所有的客户点,距离乘以订单数即可.

 
#include<bits/stdc++.h>
using namespace std;
#define int long long // 定义 int 为 long long 类型
#define endl '\n' // 定义换行符
using p = pair<int, int>; // 定义 pair<int, int> 类型的别名为 p

vector<p> d1 = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 定义四个移动方向:右、下、左、上

int n, m, k, d; // n 为方格图的大小,m 为分店数量,k 为客户数量,d 为不能经过的点的数量
vector<p> fendian; // 定义分店位置列表
struct node { // 定义客户节点结构体
    int x, y; // 客户的坐标
    int count; // 客户的订餐量
};
vector<node> kehu; // 定义客户列表
vector<vector<int>> dis; // 定义距离矩阵
int ret; // 记录最终结果

// 广度优先搜索函数,用于计算最短路径
void bfs() {
    queue<p> q; // 定义队列用于 BFS
    for (auto& xx : fendian) { // 将所有分店位置加入队列
        q.push(xx);
        dis[xx.first][xx.second] = 0; // 分店位置距离自身为 0
    }
    while (!q.empty()) {
        auto top = q.front(); // 获取队首元素
        q.pop(); // 弹出队首元素
        int i = top.first; // 当前坐标
        int j = top.second; // 当前坐标
        for (auto& dd : d1) { // 遍历四个方向
            int x = i + dd.first; // 新位置的行坐标
            int y = j + dd.second; // 新位置的列坐标
            // 判断新位置是否在边界内,且未被访问
            if (x >= 0 && x < n && y >= 0 && y < n && dis[x][y] == -1) {
                q.push({x, y}); // 将新位置加入队列
                dis[x][y] = dis[i][j] + 1; // 更新新位置的距离
            }
        }
    }
}

// 求解函数
void solve() {
    bfs(); // 调用 BFS 函数计算最短路径
    for (auto& xx : kehu) { // 遍历所有客户
        ret += dis[xx.x][xx.y] * xx.count; // 计算每个客户的配送成本并累加
    }
    cout << ret << endl; // 输出最终结果
}

// 主函数
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出

    cin >> n >> m >> k >> d; // 输入方格图的大小、分店数量、客户数量以及不能经过的点的数量
    fendian.assign(m, p()); // 初始化分店列表
    kehu.assign(k, node()); // 初始化客户列表
    dis.assign(n, vector<int>(n, -1)); // 初始化距离矩阵,设为 -1

    for (int i = 0; i < m; i++) {
        cin >> fendian[i].first >> fendian[i].second; // 输入每个分店的位置
        fendian[i].first--; // 将坐标转换为从 0 开始
        fendian[i].second--;
    }
    for (int i = 0; i < k; i++) {
        cin >> kehu[i].x >> kehu[i].y >> kehu[i].count; // 输入每个客户的位置及订餐量
        kehu[i].x--; // 将坐标转换为从 0 开始
        kehu[i].y--;
    }
    for (int i = 0; i < d; i++) {
        int x, y;
        cin >> x >> y; // 输入每个不能经过的点的位置
        x--, y--; // 将坐标转换为从 0 开始
        dis[x][y] = LLONG_MAX; // 将不能经过的点标记为无穷大
    }
    solve(); // 调用求解函数

    return 0;
}

路径之谜

题目描述

小明冒充X星球的骑士,进入了一个奇怪的城堡。

城堡里边什么都没有,只有方形石头铺成的地面。

假设城堡地面是nxn个方格。如下图所示

按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃,每走到一个新方格,就要向正北方和正西方各射一箭。(城堡

的西墙和北墙内各有12个靶子)同一个方格只允许经过一次。但不必走完所有的方格。如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?有时

是可以的,比如上图中的例子。

本题的要求就是已知简靶数字,求骑士的行走路径(测试数据保证路径唯一

输入描述

第一行一个整数N(0<N<20),表示地面有NXN个方格。

第二行N个整数,空格分开,表示北边的箭靶上的数字(自西向东)

第三行N个整数,空格分开,表示西边的箭靶上的数字(自北向南)

输出描述

输出一行若干个整数,表示骑士路径。

为了方便表示,我们约定每个小格子用一个数字代表,从西北角开始编号号:0,1,2,3-

比如,上图中的方块编号为:

0123

4567

891011

12131415

输入输出样例

示例

输入

4333

输出

045123711109131415

运行限制

最大运行时间:5s

最大运行内存:256M

1.

网格(i,j)坐标转化为数字的公式是i*col+j.

dfs遍历网格每一个点,到达每一个点的时候维护节点的信息.

节点信息,是所走路径导致的靶子上箭的数量.

2.

剪枝操作,当维护当前节点箭数量之后,当前箭数量大于对应目标箭数量,此时不需要再继续了,因为箭的数量只能增加不能减少.

如果到达了终点,箭数量有一个不对,就返回.

3.

goto flag1;

flag1:

组合起来可以传送操作.

 
#include<bits/stdc++.h>
using namespace std;
#define int long long // 定义 int 为 long long 类型
#define endl '\n' // 定义换行符
using p = pair<int, int>; // 定义 pair<int, int> 类型的别名为 p

vector<p> d = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 定义四个移动方向:右、下、左、上

int n; // 定义地面的大小 n*n
vector<int> aim1; // 北边的箭靶上的数字
vector<int> aim2; // 西边的箭靶上的数字

vector<int> nowaim1; // 当前北边的箭数量
vector<int> nowaim2; // 当前西边的箭数量
vector<int> path; // 记录骑士的路径
vector<vector<bool>> visited; // 记录每个方格是否被访问

// 深度优先搜索函数,判断是否可以通过当前路径到达终点
bool dfs(int i, int j) {
    visited[i][j] = true; // 标记当前方格已访问
    nowaim1[j]++; // 当前列的箭数量加一
    nowaim2[i]++; // 当前行的箭数量加一
    path.push_back(i * n + j); // 将当前方格编号加入路径

    // 剪枝:如果当前箭数量超过目标箭数量,回溯
    if (nowaim1[j] > aim1[j] || nowaim2[i] > aim2[i]) goto flag1;

    // 如果到达终点,检查所有箭数量是否符合目标
    if (i == n - 1 && j == n - 1) {
        for (int k = 0; k < n; k++) {
            if (nowaim1[k] != aim1[k] || nowaim2[k] != aim2[k]) goto flag1;
        }
        return true; // 路径符合要求,返回 true
    }

    // 继续搜索四个方向
    for (auto& xx : d) {
        int x = i + xx.first;
        int y = j + xx.second;
        if (x >= 0 && x < n && y >= 0 && y < n && !visited[x][y]) {
            if (dfs(x, y)) return true; // 如果找到路径,返回 true
        }
    }

    // 回溯操作
    flag1:
    nowaim1[j]--;
    nowaim2[i]--;
    visited[i][j] = false;
    path.pop_back();
    return false; // 返回 false,表示当前路径不符合要求
}

// 求解函数
void solve() {
    dfs(0, 0); // 从起点开始深度优先搜索
    for (auto& x : path) {
        cout << x << " "; // 输出路径
    }
    cout << endl;
}

// 主函数
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速输入输出

    cin >> n; // 输入地面的大小
    aim1.assign(n, 0); // 初始化北边的箭靶数字
    aim2.assign(n, 0); // 初始化西边的箭靶数字
    nowaim1.assign(n, 0); // 初始化当前北边的箭数量
    nowaim2.assign(n, 0); // 初始化当前西边的箭数量
    visited.assign(n, vector<bool>(n, false)); // 初始化访问矩阵

    for (int i = 0; i < n; i++) cin >> aim1[i]; // 输入北边的箭靶数字
    for (int i = 0; i < n; i++) cin >> aim2[i]; // 输入西边的箭靶数字

    solve(); // 调用求解函数
}

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/659499.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[oeasy]python019_ 如何在github仓库中进入目录_找到程序代码_找到代码

继续运行 &#x1f94b; 回忆上次内容 上上次 真写了万行代码 这 万行代码 都是写在明面上的 这次 使用git命令 下载了 github上面的仓库 下载仓库 之后 又该 怎么办呢&#xff1f;&#x1f914; 进入目录 首先看看 目前 在哪个目录 pwd present working directory 当前目…

论文《Planning-oriented Autonomous Driving》详细解析

论文《Planning-oriented Autonomous Driving》详细解析 摘要 现代自动驾驶系统被描述为顺序执行的模块化任务&#xff0c;即感知、预测和规划。为了执行各种任务并实现高级别智能&#xff0c;当前的方法要么为每个任务部署独立的模型&#xff0c;要么设计带有独立头的多任务范…

【YOLOv10】使用yolov10训练自己的数据集/验证 /推理 /导出模型/ONNX模型的使用

YOLOv10: 实时端到端的目标检测。 性能 YOLOv10比最先进的YOLOv9延迟时间更低&#xff0c;测试结果可以与YOLOv9媲美&#xff0c;可能会成为YOLO系列模型部署的“新选择”。 目录 1 数据准备 2 配置文件 3 训练 4 验证 5 预测 6 导出模型 7 ONNX模型的使用 官方论文地址…

高速公路边坡监测预警系统解决方案

一、概述 高速公路是国家交通大动脉&#xff0c;高速公路的安全、稳定是人民生命安全的保障。高速公路地基和边坡在线监测系统是交接高速公路运行状态的耳目&#xff0c;是保证高速公路稳定、安全保障人民生命财产安全、充分发挥高速公路国家交通大动脉的重要手段。高速边坡在线…

国产POE芯片,芯昇电子成熟量产POE芯片,在PSE端和PD端均成熟量产产品

随着技术的发展和市场的需求&#xff0c;国产POE芯片已经逐渐崭露头角。在POE技术领域&#xff0c;POE芯片分为供电设备PSE和受电设备PD&#xff0c;而选择参与802.3bt标准与以太网联盟徽标计划的厂商来生产这些芯片&#xff0c;可以确保在互操作性和合规性上更有把握。过去…

藏汉双语翻译平台,专业准确的藏语翻译工具和藏文OCR识别工具,在西藏提高工作效率的利器!

如果你正在找一款支持藏语-汉语双向翻译、操作简单、功能又丰富的藏汉在线翻译器&#xff0c;那就不得不推荐一下近期上线的藏汉翻译通小程序。在西藏工作、拉萨旅游或者写藏文作文时&#xff0c;如果你有翻译藏语的需求&#xff0c;那它&#xff0c;就能满足你&#xff0c;协助…

脑机接口:是现代医学的外挂,更是瘫痪病人的豪赌

5 月 17 日&#xff0c;马斯克公开表示&#xff0c;继今年年初首次成功将大脑芯片植入患者大脑后&#xff0c;Neuralink 正在寻找第二位受试者接受这项手术。 5 月 20 日&#xff0c;美国食品药品监督管理局 (FDA) 批准了马斯克的 Neuralink 公司为第二位患者植入脑芯片&#…

JavaSE——类和对象(三)~~继承

目录 一.继承 1.为什么需要继承 2 .继承概念 3.继承的语法格式 4.继承的特性及好处 5.父类成员访问 6.继承关系上的代码块执行顺序​​​​​​​ 二.继承与组合 一.继承 1.为什么需要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物…

2024年学浪视频怎么录屏

由于学浪最新版PC学生版客户端已经有防止录屏&#xff0c;而且录屏效率太慢&#xff0c;本文将介绍你一种高效率的工具&#xff0c;小浪助手.exe&#xff0c;它可以很轻松的将你的学浪视频下载下来 学浪下载工具我已经打包好了&#xff0c;有需要的自己下载一下 注意&#xf…

wxPython应用开发-后台线程更新大量数据到wxGrid避免ui无响应

一、问题描述 最近几天&#xff0c;我在用python开发一个数据处理的小工具。需要将xls文件中的大量数据&#xff08;少则几千行多则几万行&#xff09;读取出来后进行处理。其中一个功能是需要实现将读取到的原始数据和计算出来的结果在软件界面中以表格形式展示出来。 在pyt…

JVM学习-垃圾回收(二)

标记-清除(Mark-Sweep)算法 当堆中的有效内存空间被耗尽的时候&#xff0c;就会停止整个程序(stop the world)&#xff0c;然后进行两项工作&#xff0c;第一项则是标记&#xff0c;第二项是清除 标记&#xff1a;Collector从引用根节点开始遍历&#xff0c;标记所有被引用的…

Redis分布式存储方案

一、Redis分布式存储方案 1、哈希取余分区 ①、原理 哈希计算&#xff1a;首先&#xff0c;对每个键&#xff08;key&#xff09;进行哈希计算&#xff0c;得到一个整数哈希值&#xff08;hash value&#xff09;。取余操作&#xff1a;将这个哈希值对服务器数量进行取余操作…

Ansible03-Ansible Playbook剧本详解

目录 写在前面5. Ansible Playbook 剧本5.1 YAML语法5.1.1 语法规定5.1.2 示例5.1.3 YAML数据类型 5.2 Playbook组件5.3 Playbook 案例5.3.1 Playbook语句5.3.2 Playbook1 分发hosts文件5.3.3 Playbook2 分发软件包&#xff0c;安装软件包&#xff0c;启动服务5.3.3.1 任务拆解…

数分之SQL查询电商数据案例

1,Python连接SQL数据库 以下是使用Python连接MySQL数据库并进行操作的示例代码&#xff1a; import random import time import pymysql# 定义名字数据 xing ["王", "李", "张", "刘", "陈", "杨", "黄&q…

【火猫CS2】fantic取代C9参加YaLLa指南针

1、近日YaLLa Compass主办方宣布,由于Could9战队未能在截止日期前提交完整的参赛阵容,fantic战队将取代其参赛。该比赛将在阿联酋阿布扎比举行,总奖金40万美元。 最近一段时间Cloud9战队最近将electroNic转会至VP,又下放了HObbit和Perfecto,队伍因没有完整阵容已被迫退出EPL S1…

服装服饰商城小程序的作用是什么

要说服装商家&#xff0c;那数量是非常多&#xff0c;厂家/经销门店/小摊/无货源等&#xff0c;线上线下同行竞争激烈&#xff0c;虽然用户群体广涵盖每个人&#xff0c;但每个商家肯定都希望更多客户被自己转化&#xff0c;渠道运营方案营销环境等不可少。 以年轻人为主的消费…

前端破圈用Docker开发项目

为什么要用 Docker 开发 &#x1f914; 直接在系统上开发不香吗&#xff1f;香&#xff0c;但是 Docker 有下面4香 环境依赖管理&#xff1a;Docker 容器可以管理所有依赖项&#xff0c;例如前端里面的 node 和 npm 版本&#xff0c;不需要在本地安装和维护这些依赖项 隔离&a…

【刷题(12)】图论

一、图论问题基础 在 LeetCode 中&#xff0c;「岛屿问题」是一个系列系列问题&#xff0c;比如&#xff1a; 岛屿数量 &#xff08;Easy&#xff09;岛屿的周长 &#xff08;Easy&#xff09;岛屿的最大面积 &#xff08;Medium&#xff09;最大人工岛 &#xff08;Hard&…

高效记录收支明细,预设类别账户,智能统计财务脉络,轻松掌握个人财务!

收支明细管理是每位个人或企业都必须面对的财务任务&#xff0c;财务管理已经成为我们生活中不可或缺的一部分。如何高效记录收支明细&#xff0c;预设类别账户&#xff0c;智能统计财务脉络&#xff0c;轻松掌握个人财务&#xff1f;晨曦记账本为您提供了完美的解决方案&#…

windows环境redis未授权利用手法总结

Redis未授权产生原因 1.redis绑定在0.0.0.0:6379默认端口&#xff0c;直接暴露在公网&#xff0c;无防火墙进行来源信任防护。 2.没有设置密码认证&#xff0c;可以免密远程登录redis服务 漏洞危害 1.信息泄露&#xff0c;攻击者可以恶意执行flushall清空数据 2.可以通过ev…