洛谷p1194买礼物:使用Kruskal算法和prim算法实现

题目描述

又到了一年一度的明明生日了,明明想要买 𝐵B 样东西,巧的是,这 𝐵B 样东西价格都是 𝐴A 元。

但是,商店老板说最近有促销活动,也就是:

如果你买了第 𝐼I 样东西,再买第 𝐽J 样,那么就可以只花 𝐾𝐼,𝐽KI,J​ 元,更巧的是,𝐾𝐼,𝐽KI,J​ 竟然等于 𝐾𝐽,𝐼KJ,I​。

现在明明想知道,他最少要花多少钱。

输入格式

第一行两个整数,𝐴,𝐵A,B。

接下来 𝐵B 行,每行 𝐵B 个数,第 𝐼I 行第 𝐽J 个为 𝐾𝐼,𝐽KI,J​。

我们保证 𝐾𝐼,𝐽=𝐾𝐽,𝐼KI,J​=KJ,I​ 并且 𝐾𝐼,𝐼=0KI,I​=0。

特别的,如果 𝐾𝐼,𝐽=0KI,J​=0,那么表示这两样东西之间不会导致优惠。

注意 𝐾𝐼,𝐽KI,J​ 可能大于 𝐴A。

输出格式

一个整数,为最小要花的钱数。

输入输出样例

输入 #1复制

1 1
0

输出 #1复制

1

输入 #2复制

3 3
0 2 4
2 0 2
4 2 0

输出 #2复制

7

说明/提示

样例解释 22。

先买第 22 样东西,花费 33 元,接下来因为优惠,买 1,31,3 样都只要 22 元,共 77 元。

(同时满足多个“优惠”的时候,聪明的明明当然不会选择用 44 元买剩下那件,而选择用 22 元。)

数据规模

对于 30%30% 的数据,1≤𝐵≤101≤B≤10。

对于 100%100% 的数据,1≤𝐵≤500,0≤𝐴,𝐾𝐼,𝐽≤10001≤B≤500,0≤A,KI,J​≤1000。

2018.7.25新添数据一组

使用Kruskual算法(思路)

可以使用一个数组存储图,在数组中存储每条边和权值,将数组按照每条边的权值一从小到大的顺序排序,再使用并查集判断是否公用一个父亲,将不是同一集合中的点归变,直到最后集合中包含所有的点(注意题目中的优惠价格肯能还没有普通值A划算(黑心bushi),所以要取较小的值)

#include<iostream>
#include<algorithm>
using namespace std;
int a, b, cnt;
int f[505];
typedef struct {    //数组存储每条边信息
	int fis, nex, val;
}node;
node edge[250000];
bool cmp(node x, node y)
{
	return x.val < y.val;
}
int find(int x)		//并查集查找
{
	if (x == f[x]) {
		return x;
	}
	else {
		return f[x] = find(f[x]);
	}
}
int main()
{
	cin >> a >> b;
	for (int i = 1; i <= b; i++) {
		for (int j = 1; j <= b; j++) {
			int h;
			cin >> h;
			if (i == j) continue;		  //不考虑自身
			edge[++cnt].fis = i;
			edge[cnt].nex = j;
			edge[cnt].val = min(h, a);    //存储最小的值
			if (edge[cnt].val == 0) {
				edge[cnt].val = a;        //没有优惠则改为默认值
			}
		}
		f[i] = i;
	}
	sort(edge, edge + cnt, cmp);         //排序
	int h = 1, ans = 0;
	for (int i = 0; i < cnt; i++) {
		if (h < b) {
			int j = find(edge[i].fis);
			int k = find(edge[i].nex);
			if (j != k) {
				f[j] = f[k];
				ans += edge[i].val;
				h++;
			}
		}
	}
	cout << ans + a;					//最后的值加上第一个
	return 0;
}

使用prim算法:

使用链式前向星存储图(无向图存两次),然后遍历每一个节点,将与节点相连的边存入一个优先队列中(小根堆),然后出队,根据出队的最小边找到下一节点的相连的边,再依次入队,进行一个循环,直到所有节点都遍历过一遍或者队空则结束。

#include<iostream>
#include<queue>
using namespace std;
typedef struct {
	int next, to, val;
}node;
node edge[500000];
int head[505];
int cnt, a, b, ans;
int k = 1;
void add_edge(int x, int y, int z)		//链式前向星存储图
{
	edge[++cnt].to = y;
	edge[cnt].val = z;
	edge[cnt].next = head[x];
	head[x] = cnt;
}
typedef pair<int, int> pll;
priority_queue<pll, vector<pll>, greater<pll>> q;		//建立优先队列,第一个值表示权值,第二个表示边号
int dis[505];											//标记每个点是否进入
void prim()
{
	dis[1] = 1;			//标记第一个点
	for (int i = head[1]; i != 0; i = edge[i].next) {
		q.push(make_pair(edge[i].val, i));
	}
	while (!q.empty() && k < b) {
		int x = q.top().first;		//权值
		int y = q.top().second;		//出队的边指向的下一节点
		q.pop();
		if (dis[edge[y].to] == 0) {
			ans += x;
			k++;
			dis[edge[y].to] = 1;
			for (int i = head[edge[y].to]; i != 0; i = edge[i].next) {
				q.push(make_pair(edge[i].val, i));
			}
		}
	}
}
int main()
{
	cin >> a >> b;
	for (int i = 1; i <= b; i++) {
		for (int j = 1; j <= b; j++) {
			int x;
			cin >> x;
			if (i == j) continue;
			int val = min(x, a);
			if (x == 0) val = a;
			add_edge(i, j, val);  //无向图存两次
			add_edge(j, i, val);
		}
	}
	prim();
	cout << ans + a;
	return 0;
}

 

最后代码上传结果 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值