1、车间检查---cdoj3164
多源点最短路径 (Multiple Source Shortest Path)问题
定义:计算从多个源点到图中所有其他节点的最短路径。(Dijkstra是算一个源点到其他节点的最短路径)。
方法:一种常见的方法是添加一个虚拟节点 S′,并将这个节点连接到所有的源点,边的权重为零。然后使用单源最短路径算法(如Dijkstra或Bellman-Ford算法)计算从这个虚拟节点出发的最短路径[ 想成其他节点到这个虚拟节点的距离,后续更好理解 ]。
解题思路:
关于本题,可以抽象为:生产相同零件的车间算做一个虚拟节点S'(就是将生产相同零件的车间用权值为0的边连接起来,连接后的整体看做一个虚拟节点。ps:并不是真正的连接,只是在后续使用BFS入队时,生产相同零件的车间一起入队即可)。有K个不同零件,则有K种不同的虚拟节点。然后遍历每一个虚拟节点,
关于建图方式。路径权值都是1,无向图。所以我们可以采取BFS的方式(因为权值为1,用这个简单点)——>就会使用到队列,就自然而然地想到使用vector的方式进行建图,并用一个Bool vis数组表示节点是否被访问过。【如果有不同权值,就要考虑用dij算法了】。并将结果保存
经过bfs求出了每一个虚拟节点到其他节点的的最短路径。然后根据题意我们要算最短开销(看下面的样例进行解释)。
输入样例:
7 6 3 2
1 2 3 3 2 2 1
1 2
2 3
3 4
2 5
5 6
6 7
邻接表存图:
adj = {
0: [1],
1: [0, 2, 4],
2: [1, 3],
3: [2],
4: [1, 5],
5: [4, 6],
6: [5]
}
BFS结果:(各个节点到虚拟节点的距离)
color_to_distances[1] = [0, 1, 2, 3, 2, 1, 0]
color_to_distances[2] = [1, 0, 1, 2, 0, 0, 1]
color_to_distances[3] = [2, 1, 0, 0, 2, 3, 4]
计算最小开销:
一号车间:看第一竖行,分别表示了1号车间到1,2,3号虚拟节点的距离,0+1=1
二号车间:看第二竖行,分别表示了2号车间到1,2,3号虚拟节点的距离,0+1=1
……………………
五号车间:看第五竖行,分别表示了5号车间到1,2,3号虚拟节点的距离,0+2=2
六号车间:看第六竖行,分别表示了6号车间到1,2,3号虚拟节点的距离,0+1=1
七号车间:看第七竖行,分别表示了7号车间到1,2,3号虚拟节点的距离,0+1=1
最终结果:
1
1
1
2
2
1
1
代码实现:
需要一个bfs函数,一个min_cost函数,一个main函数。
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,k,s;
vector<int> bfs(const vector<vector<int>>& adj, const vector<int>& sources){
vector<int> dist(n, -1); // 初始化距离数组,-1表示未访问
queue<int> q;
// 将所有起点加入队列
for (int source : sources) {
dist[source] = 0;
q.push(source);
}
// BFS遍历
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (dist[v] == -1) { // 如果 v 尚未访问
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
return dist;
}
vector<int> min_cost( const vector<int>& colors, const vector<pair<int, int>>& edges){
//处理为无向图
vector<vector<int>> adj(n); // 图的邻接表表示
for (const auto& edge : edges) {
adj[edge.first].push_back(edge.second);
adj[edge.second].push_back(edge.first);
}
//处理k个虚拟节点
vector<vector<int>> color_to_distances(k + 1, vector<int>(n, -1));//存储到其他节点到虚拟节点的距离
for (int c = 1; c <= k; ++c) { //遍历每一种颜色
vector<int> sources; //每次都重新初始化了sources,清空了上一个颜色的车间
for (int i = 0; i < n; ++i) { //遍历每一个节点看是不是这种颜色,从而确保同种颜色,一次性入vector
if (colors[i] == c) {
sources.push_back(i);
}
}
if (!sources.empty()) { //如果sources不是空,就执行
color_to_distances[c] = bfs(adj, sources); // 计算每种颜色的最短距离
}
}
//计算最小开销
vector<int> result(n, 0);
for (int i = 0; i < n; ++i) { //遍历每个节点
vector<int> distances;
for (int c = 1; c <= k; ++c) { //遍历每个颜色
if (color_to_distances[c][i] != -1) { // 如果有距离记录
distances.push_back(color_to_distances[c][i]);
}
}
sort(distances.begin(), distances.end()); // 排序距离
for (int j = 0; j < s && j < distances.size(); ++j) {
result[i] += distances[j]; // 取最小的 s 个距离求和
}
}
return result;
}
int main(){
cin>>n>>m>>k>>s;
vector<int> color(n); //初始化n个int类型的用于存生产零件类型,将一种零件看做一个color
for(int i=0;i<n;i++){
cin>>color[i];
}
//读入边信息
vector<pair<int,int>> edge(m);
for (int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
edge[i] = {u - 1, v - 1}; // 假设输入的节点编号是从1开始的,转换为从0开始
}
vector<int> result = min_cost( color, edge);
for (int cost : result) {
cout << cost << endl;
}
return 0;
}