题目描述
一些公司将在 Byteland 举办商品交易会(or博览会?)。在Byteland有
n
n
n 个城市,城市间有
m
m
m 条双向道路。当然,城镇之间两两连通。
Byteland生产的货物有
k
k
k 种类型,每个城镇只生产一种。
为了举办商品交易会,你必须至少带来
s
s
s 种不同类型的商品。将货物从
u
u
u 镇带到城镇
v
v
v 将花费
d
(
u
,
v
)
d(u,v)
d(u,v) 的费用,其中
d
(
u
,
v
)
d(u,v)
d(u,v) 是从
u
u
u 到
v
v
v 的最短路径的长度。
路径的长度是这个路径中的道路的数量。
组织者将支付所有的运输费用,但他们可以选择从哪些城镇带来货物。现在他们想计算每个城镇举办商品交易会的最小费用。
分析
如果对每个点都跑一遍 BFS
的话,时间复杂度太高,达到了
O
(
n
2
)
O(n^2)
O(n2),而
n
≤
1
0
5
n \le 10^5
n≤105 肯定是不行的,我们注意到商品总数小于
100
100
100,所以我们可将研究对象转为商品。
维护
d
i
s
t
i
,
j
dist_{i,j}
disti,j 表示商品
j
j
j 到城镇
i
i
i 的最短距离,枚举每个商品,将每个买商品
j
j
j 的城镇压入队列,跑多起点广搜1。
枚举每个城镇,将
d
i
s
t
i
dist_i
disti 从小到大排序,取前
s
s
s 项相加,输出即为第
i
i
i 个城镇举办商品交易会的最小费用。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, m, k, s, a[N], vis[N], dist[N][105];//dist[i][j]:第j种货物到i的最短距离
vector <int> nbr[N];
void bfs(int Type){
memset(vis, 0, sizeof vis);
queue <int> q;
for(int i = 1; i <= n; i ++){
if(a[i] == Type){//将生产Type种货物的城市放入队列
vis[i] = true;
dist[i][Type] = 0;
q.push(i);
}
}
while(!q.empty()){
int x = q.front();
q.pop();
for(int i = 0; i < nbr[x].size(); i ++){
int nxt = nbr[x][i];
if(!vis[nxt]){//如果下一个点没走过
q.push(nxt);
vis[nxt] = true;
dist[nxt][Type] = dist[x][Type] + 1;//更新最短路径
}
}
}
}
int main(){
cin >> n >> m >> k >> s;
for(int i = 1; i <= n; i ++){
cin >> a[i];
}
for(int i = 1; i <= m; i ++){
int u, v;
cin >> u >> v;
nbr[u].push_back(v);//添加双向边
nbr[v].push_back(u);
}
for(int i = 1; i <= k; i++){//枚举k种货物,并对其跑bfs
bfs(i);
}
for(int i = 1; i <= n; i ++){
int ans = 0;
sort(dist[i] + 1, dist[i] + k + 1);//将距离排序
for(int j = 1; j <= s; j ++){//将前s小的距离计入答案
ans += dist[i][j];
}
cout << ans << " ";
}
return 0;
}