题目描述
CSU镇上有n个商店,n个商店有m条双向小路相连,在这n个商店里共有k种不同商品,每个商店只有一种商品,每条路的权重都为1。现问你从每个商店出发,买够k种商品中的s种商品所需的最小代价,每个商店可以同时派出多个人买不同商品,买够即可。
输入
输入包含多组测试用例。
对于每一组输入包含四个数字n ,m, k,s (1<=n<=m<=1e5 , 1<=s<=k<=min(n,100))
分别代表商店数,小路数,商品种数,需要的商品数。
接下来n个数 a1,a2...an (1<=ai<=k),ai代表第i个商店的商品编号。
接下来m行小路(u,v),u≠v,代表商店u和v之间有小路连接。
输出
输出n个数字,第i个数字代表从商店i出发买够s种商品所需的最小代价。
样例输入
5 5 4 3
1 2 4 3 2
1 2
2 3
3 4
4 1
4 5
7 6 3 2
1 2 3 3 2 2 1
1 2
2 3
3 4
2 5
5 6
6 7
样例输出
2 2 2 2 3
1 1 1 2 2 1 1
分析:
一开始我写的是从每个商店出发,利用bfs求最小代价,但是这个方面一个oj过了,另一个oj超时。于是跟别人学习了多源bfs,对于每一种商品,记录从每个商店出发到能够购买到该商品的最短距离。
#include<bits/stdc++.h>
using namespace std;
int n, m, k, s;
const int N = 1e5 + 10;
int goods[N];
vector<int> shop[N];
int ans[N][110];
/*
多源bfs:此题存在n远大于k的情况,如果每次都从n出发,需要调用多次bfs
但是如果从k种商品出发,调用bfs的次数就会大大减少,提高效率
多源bfs和单源bfs同样都是广搜,只不过多源bfs会从多个点同时出发,一层层往外扩
*/
void goShopping(int kind){
//多源bfs
queue<int> q;
for(int i = 1; i <= n; i++){
//记录贩卖该类商品的商店
if(goods[i] == kind){
q.push(i);
ans[i][kind] = 0;
}
}
while(!q.empty()){
int front = q.front(); q.pop();
for(int i = 0; i < shop[front].size(); i++){
int v = shop[front][i];
//如果ans[v][kind] == -1,说明ans[front][kind] + 1就是最短距离
//如果ans[v][kind] != -1,在广搜的情况下,已是最短距离,无须比较,无须更新
if(ans[v][kind] == -1){
ans[v][kind] = ans[front][kind] + 1;
q.push(v);
}
}
}
}
int main(){
while(~scanf("%d%d%d%d", &n, &m, &k, &s)){
int a, b;
memset(ans, -1, sizeof(ans));
for(int i = 1; i <= n; i++){
shop[i].clear();
scanf("%d", &goods[i]);
}
for(int i = 0; i < m; i++){
scanf("%d%d", &a, &b);
shop[a].push_back(b);
shop[b].push_back(a);
}
for(int i = 1; i <= k; i++){
goShopping(i);
}
int res;
for(int i = 1; i <= n; i++){
res = 0;
sort(ans[i] + 1, ans[i] + 1 + k);
for(int j = 1; j <= s; j++)
res += ans[i][j];
cout << res << " ";
}
cout << endl;
}
return 0;
}