CSP历年复赛题-P1078 [NOIP2012 普及组] 文化之旅

原题链接:https://www.luogu.com.cn/problem/P1078

题意解读:1~n个国家,每个国家有自己的文化,不同国家文化可以相同,要从起点遍历到终点,已经学习过的文化不能重复学习,已经学习过的文化被某个文化歧视的国家也不能遍历,且不同国家之间有边,边有不同的距离,计算从起点到终点的最短路径。

解题思路:

分析题意,要遍历起点到终点,有两种方式:DFS、BFS,BFS通常用来计算的最短路要求边长都一样,也就是主要计算的最短路是第几层。

而本题每条边距离不等,因此考虑通过DFS所有所有的路径,从中求最短的距离。

分析复杂度:点最多100,边最多10000,文化最多100,DFS搜索有所路径的复杂度是O(100^100),每次到一个点要判断是否歧视已经学习的所有文化,总体时间复杂度在O(100^100*100),会超时。先实现代码,然后再优化!

60分代码:

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

int n, k, m, s, t;
int diss[105][105]; //文化歧视表
int c[105]; //国家的文化
struct node
{
    int u; //起点
    int v; //终点
    int d; //u到v的距离
};
vector<node> g[105]; //领接表
bool vis[105];
int ans = INT_MAX;

void dfs(int u, int dist)
{
    if(u == t)
    {
        ans = min(ans, dist);
        return;
    }
    for(int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i].v;
        if(vis[c[v]]) continue; //如果文化已经学过,不重复学
        bool qishi = false;
        for(int j = 1; j <= k; j++)
        {
            if(vis[j] && diss[c[v]][j]) //已经学到的文化被v国文化歧视
            {
                qishi = true;
                break;
            }
        }
        if(qishi) continue; //被文化歧视,则不能经过
        vis[c[v]] = 1;
        dfs(v, dist + g[u][i].d);
        vis[c[v]] = 0;
    }
}

int main()
{
    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 >> diss[i][j];
    int u, v, d;
    for(int i = 1; i <= m; i++)
    {
        cin >> u >> v >> d;
        g[u].push_back({u, v, d});
        g[v].push_back({v, u, d});
    }
    vis[c[s]] = 1; //标记已学到起点的文化
    dfs(s, 0);
    if(ans != INT_MAX) cout << ans;
    else cout << -1;

    return 0;
}

此题性能提升的关键在暴搜,需要考虑一种有效的剪枝策略,减少搜索的层级。

只需要用数组f[]记录每次遍历到的点时的距离,f[u] = dist

在dfs开始处,如果当前再次走到u点时的距离dist >= 之前保存过的f[u],则没有必要在继续走了,可以提前return

这样一来就大大减少的暴搜的次数。

100分代码:

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

int n, k, m, s, t;
int diss[105][105]; //文化歧视表
int c[105]; //国家的文化
struct node
{
    int u; //起点
    int v; //终点
    int d; //u到v的距离
};
vector<node> g[105]; //领接表
bool vis[105];
int f[105]; //记录到某个点的距离
int ans = INT_MAX;

void dfs(int u, int dist)
{
    if(dist >= f[u]) return;
    f[u] = dist;
    if(u == t)
    {
        ans = min(ans, dist);
        return;
    }
    for(int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i].v;
        if(vis[c[v]]) continue; //如果文化已经学过,不重复学
        bool qishi = false;
        for(int j = 1; j <= k; j++)
        {
            if(vis[j] && diss[c[v]][j]) //已经学到的文化被v国文化歧视
            {
                qishi = true;
                break;
            }
        }
        if(qishi) continue; //被文化歧视,则不能经过
        vis[c[v]] = 1;
        dfs(v, dist + g[u][i].d);
        vis[c[v]] = 0;
    }
}

int main()
{
    memset(f, 0x3f, sizeof(f));
    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 >> diss[i][j];
    int u, v, d;
    for(int i = 1; i <= m; i++)
    {
        cin >> u >> v >> d;
        g[u].push_back({u, v, d});
        g[v].push_back({v, u, d});
    }
    vis[c[s]] = 1; //标记已学到起点的文化
    dfs(s, 0);
    if(ans != INT_MAX) cout << ans;
    else cout << -1;

    return 0;
}

 

posted @ 2024-05-31 17:03  五月江城  阅读(55)  评论(0编辑  收藏  举报