题目描述
又到了一年一度的明明生日了,明明想要买 𝐵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;
}
最后代码上传结果