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;
}