You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.1 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 452. 文化之旅

一、题目描述

有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超过一次(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家)。

不同的国家可能有相同的文化。

不同文化的国家对其他文化的看法不同,有些文化会排斥外来文化(即如果他学习了某种文化,则他不能到达排斥这种文化的其他国家)。 

注:仔细阅读,整明白是谁排斥谁,别整反了。

现给定各个国家间的地理关系,各个国家的文化,每种文化对其他文化的看法,以及这位使者游历的起点和终点(在起点和终点也会学习当地的文化),国家间的道路距离,试求从起点到终点最少需走多少路。

输入格式 第一行为五个整数 NKMST,每两个整数之间用一个空格隔开,依次代表国家个数(国家编号为 1N),文化种数(文化编号为 1K),道路的条数,以及起点和终点的编号(保证 S 不等于 T)。

第二行为 N 个整数,每两个整数之间用一个空格隔开,其中第 i 个数 C_i,表示国家 i 的文化为 C_i。 

接下来的 K 行,每行 K 个整数,每两个整数之间用一个空格隔开,记第 i 行的第 j 个数为 a_{i,j}a_{i,j}=1 表示文化 i 排斥外来文化 ji 等于 j 时表示排斥相同文化的外来人),a_{i,j}=0 表示不排斥(注意 i 排斥 j 并不保证 j 一定也排斥 i)。

接下来的 M 行,每行三个整数 uvd,每两个整数之间用一个空格隔开,表示国家 u 与国家 v 有一条距离为 d 的可双向通行的道路(保证 u 不等于 v两个国家之间可能有多条道路)。

输出格式 输出只有一行,一个整数,表示使者从起点国家到达终点国家最少需要走的距离数(如果无解则输出 1)。

数据范围 2≤N≤100,1≤K≤100,1≤M≤N2,1≤Ci≤K,1≤u,v≤N,1≤d≤1000,1≤S,T≤N,S≠T

样例 输入样例:

2 2 1 1 2
1 2
0 1
0 0
1 2 10

输出样例

10

二、算法

暴力+剪枝+ floyd

有文化限制 不能直接最短路去做

数据范围较小 可以用floyd水过

启发式剪枝 不考虑限制的最短路距离一定是小于等于考虑限制的距离 d[i][j]表示i->j的最短距离 ans表示程序求出的解 dist表示从起点到u的距离 那么

\large dist + d[u][T] <= ans

那么dist + d[u][T] >= ansans就是无用的,可以continue

再加一点玄学优化,倒着搜,正着搜会被卡,其余的直接暴力

三、实现代码

#include <bits/stdc++.h>

using namespace std;
const int N = 110;
const int INF = 0x3f3f3f3f;
int n, K, m, S, T;
int c[N];         // 文化
int cg[N][N];     // 互斥关系
int g[N][N];      // 地图,不一定连通
int d[N][N];      // floyd用的多源最短路数组
int path[N], top; // 记录已经学过的文化
int ans = INF;    // 答案

void dfs(int u, int dist) {            // 从u出发dist:已经走的步数
    if (dist + d[S][u] >= ans) return; // 启发式剪枝,如果已经走的步数+从u到S的最短距离已经大于目前的最小答案那么此路不通
    if (u == S) {                      // 如果走到了S收集答案
        ans = dist;
        return;
    }

    // 还没有走到起点,从哪个点走过来的
    for (int i = 1; i <= n; i++) {
        if (g[i][u] < INF) {                // 可能从i点过来的
            bool flag = true;               // 文件上没有排斥
            for (int j = 0; j < top; j++) { // 遍历一下所有已经走过的文化,是不是存在文化上的冲突
                if (cg[path[j]][c[i]]) {    // 如果i国家的文化与后面已知国家的文化冲突那么一定不是从i过去的
                    flag = false;           // 不可能从i走到u
                    break;
                }
            }
            if (flag) {                 // 文化上没有排斥
                path[top++] = c[i];     // i号城市的文化c[i]进入数组
                dfs(i, dist + g[i][u]); // 继续反向搜索
                top--;                  // 回溯
            }
        }
    }
}

int main() {
    // 加快读入
    ios::sync_with_stdio(false), cin.tie(0);

    // 一般在数据量大于10W时考虑用scanf,如果scanf过不去再考虑使用快读
    cin >> n >> K >> m >> S >> T;

    // 先读入文化
    for (int i = 1; i <= n; i++) cin >> c[i];
    for (int i = 1; i <= K; i++) // 读入排斥关系
        for (int j = 1; j <= K; j++)
            cin >> cg[i][j];
    // 每个文化排斥自己
    for (int i = 1; i <= K; i++) cg[i][i] = 1;

    // 两个国家之间可能有多条道路,可能有重边
    memset(g, 0x3f, sizeof g);                // 任意两点间距离设置为正无穷,表示不连通
    for (int i = 1; i <= n; i++) g[i][i] = 0; // 一开始每个国家都与自己连通
    // 读入所有的边
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c);
    }
    // Floyd 用于启发式剪枝,提前预处理出任意两点间的最短距离
    memcpy(d, g, sizeof d);
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

    path[top++] = c[T];       // 直播时忘记加入终点了,初始化终点
    dfs(T, 0);                // 从后往前搜索,玄学倒序,专克出题人
    if (ans == INF) ans = -1; // 没有任何一个能走到S的说明无解
    cout << ans << endl;
    return 0;
}