小镇购物(多源bfs)

题目描述

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

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值