博客借鉴:https://blog.csdn.net/qq_36553623/article/details/78524754
题目:
垃圾佬抓宠物 TimeLimit:1000MS MemoryLimit:128MB 64-bit integer IO format:%lld Problem Description 垃圾佬也有头痛的时候,这不,垃圾佬的GF又缠着他了…… 静静:“Darling!” 垃圾佬:“怎么?” 静静:“我玩那个OWO更新了哦~新增的宠物系统好可爱啊~不过抓宠物要用好多魔法豆哎” 垃圾佬:“那个魔法豆是打怪得的?” 静静:"不是哦,用RMB 买才得。" 垃圾佬:“哎,现在的网游,就是会骗钱。” 静静:“怎么能这么说人家呢,那些宠物都好可爱的,花点钱算什么嘛。我打算把所有的宠物都抓到哦~” 垃圾佬:“又说没有魔法豆,难道……” 静静:“我把你的钱都拿来冲成魔法豆了哦~你看你看,这些宠物都好可爱的,你不会有意见吧?^_^” 垃圾佬:“……没有。” 静静:“不要苦瓜着脸嘛,要不这样吧,你帮我把所有的宠物都抓起来,剩下的魔法豆我再帮你换回RMB 好不好?^_^” 垃圾佬:“……好……好吧” 这下惨了,垃圾佬现在好后悔在静静面前炫耀自己的钱包啊…… 静静做事,向来说一不二,抓齐宠物恐怕是免不了的了。 不过,垃圾佬发现,这个网游的抓宠物系统是这样设定的: 抓特定的某种宠物需要花费特定的魔法豆。 另外,还可以通过一种宠物的呼唤能力来抓取另一种宠物,当然,要让宠物发挥它的呼唤能力需要两个条件: 第一,它要是你的宠物……人家还野生呢总不会听你的话陷害好友吧。 第二,你需要喂给你的宠物一定的魔法豆,它才有力气施展它的能力。 当另一只宠物被呼唤过来之后,你不费什么力气就可以把它抓住,也就不必再向系统付额外的魔法豆了。 注意,一种宠物能呼唤另一种宠物是因为他们心灵相通,所以如果A宠物能呼唤B宠物,那么B宠物也一定能呼唤A宠物。 而且,垃圾佬仔细研究之后发现,可能是OWO的程序员懒得再设置数据,A 宠物呼唤B宠物需要的魔法豆和B宠物呼唤A宠物需要的魔法豆是相等的。 垃圾佬的目标,当然是要算出最少要花费多少魔法豆啦~ 垃圾佬拿出纸笔,算啊算啊算啊算,就在垃圾佬算出来的一刹那,静静温柔的对垃圾佬说:“Darling,你会不会觉得我很败家啊……其实我也知道我不应该用你的钱来冲魔法豆的” 垃圾佬心中一喜,抬头看着静静,静静说:“要不你不用抓齐,少抓一只吧。” 垃圾佬呆住了……这是一个多么体贴的GF啊!许久许久,垃圾佬又拿起纸笔重新算过。 Input 第一行一个数字n,代表网游里一共有n 种宠物。 第二行有n 个数字,用空格隔开,依次说明直接抓取第1,2,3,4……n 种宠物需要多 少魔法豆 第三行一个C,表示游戏一共设定了C 对宠物心灵相通,可以互相呼唤。 接下来C 行,每行都有三个数字a,b 和w。代表第a 种和第b 种宠物可以互相呼唤, 呼唤前需要给a 宠物或b 宠物喂w 魔法豆。 n≤250,n∈N+ 0<=c<=n*(n-1)/2 所有数字(含结果)<2^31 Output 只有一行,满足MM 需求最少要花费多少魔法 SampleInput 3 10 10 10 3 1 2 7 1 3 1 2 3 3 SampleOutput 11 样例说明:先抓第一只,然后用第一只呼唤第三只。
题目大意:给你 n 个点,m 条边,其中选择任意一点需要消耗一定代价,选择效果为点亮这一点,
选择任意一条边也需要消耗一定代价,选择前提为此边顶点有一点已经点亮,选择效果为点亮另外一个顶点,
问:如何以最小代价点亮 n-1 个点。
一,添加点 与 删除点
添加点:将 点亮一个点 看成是图外一点 连接这一点,这操作太秀了。
原先点 是从 1~n,这样再设一点 0 就可以了。
删除点:由于题目要求可以不要 一点,这一点可以任意选择,因为 n 比较小,
于是直接暴力枚举 1~n 的每个点。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<algorithm> #include<iostream> using namespace std; #define N (int)1e5+10 #define MIN(x,y) (x<y?x:y) int n, m, sum; int p[N], a[N]; struct edge { int x, y, w; }e[N]; int cmp(const edge&a, const edge&b) { return a.w < b.w; } int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } int join(int x, int y) { x = find(x), y = find(y); if (x == y) return 0; p[x] = y; return 1; } void init() { for (int j = 0; j <= n; j++) // 添加一个 结点 0 p[j] = j; sum = 0; } int main(void) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); scanf("%d", &m); for (int i = 1; i <= m; i++) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].w); for (int i = 1; i <= n; i++) { e[i + m].x = 0; e[i + m].y = i; e[i + m].w = a[i]; } int ans = ~0U >> 1; // 最大值 sort(e + 1, e + m + n + 1, cmp); for (int i = 1; i <= n; i++) { init(); for (int j = 1; j <= m + n; j++) { if (e[j].x == i || e[j].y == i) //删除 1~n 中的一个节点 continue; if (join(e[j].x, e[j].y)) sum += e[j].w; } ans = MIN(ans, sum); } printf("%d ", ans); system("pause"); return 0; }
二,错误代码
① 刚开始没注意看题,以为 选择点 一定比 选择边 代价少,于是将 选择边 和 选择点 分开计算,默认优先 选择点
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<algorithm> using namespace std; #define N 255 int p[N], b[N]; struct edge { int from, to, w; }e[90000]; struct node { int w, vis; }a[N]; int cmp(const edge& a, const edge& b) { return a.w < b.w; } int find(int x) { if (x != p[x]) p[x] = find(p[x]); return p[x]; } int join(int x, int y) { x = find(x), y = find(y); if (x == y) return 0; p[x] = y; return 1; } int main(void) { int n, nn = 2147483647; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i].w); if (a[i].w < nn) nn = a[i].w; p[i] = i; } int m; scanf("%d", &m); for (int i = 1; i <= m; i++) { scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].w); } sort(e + 1, e + m + 1, cmp); int f = 0; if (n - 2 <= m) { m = n - 2; f = 1; } else m = n - 1; int sum = 0, t = 0; for (int i = 1; i <= m; i++) { if (join(e[i].from, e[i].to)) { sum += e[i].w; a[e[i].from].vis = a[e[i].to].vis = 1; } } if (f) { printf("%d ", sum + nn); system("pause"); return 0; } int bb = 0; nn = 2147483647; for (int i = 1; i <= n; i++) { if (a[i].vis == 0) b[++bb] = a[i].w; if (a[i].vis == 1) if (a[i].w < nn) nn = a[i].w; } sort(b + 1, b + bb + 1); for (int i = 1; i <= bb - 1; i++) { sum += b[i]; } printf("%d ", sum + nn); system("pause"); return 0; } /* 4 1 2 3 4 1 2 3 1 3 5 1 2 3 4 5 2 1 2 1 1 3 1 5 1 2 3 4 5 6 1 2 1 1 3 1 1 4 1 1 5 1 2 3 2 3 4 3 */
② 在删除点的时候,没有选择枚举,而是直接选出 n-2 条边,最后第 n-1 条边再特殊判断一下,保证 0 点一定选中。想不出来为什么不可以。(╥╯^╰╥)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<algorithm> #include<iostream> using namespace std; #define N (int)1e5+10 #define MIN(x,y) (x<y?x:y) int n, m, sum; int p[N], a[N], vis[N]; struct edge { int x, y, w; }e[N]; int cmp(const edge&a, const edge&b) { return a.w < b.w; } int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } int join(int x, int y) { x = find(x), y = find(y); if (x == y) return 0; p[x] = y; return 1; } void init() { for (int j = 0; j <= n; j++) // 添加一个 结点 0 p[j] = j; sum = 0; } int main(void) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); scanf("%d", &m); for (int i = 1; i <= m; i++) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].w); for (int i = 1; i <= n; i++) { e[i + m].x = 0; e[i + m].y = i; e[i + m].w = a[i]; } int ans = ~0U >> 1; // 最大值 sort(e + 1, e + m + n + 1, cmp); init(); int f = 0, t = 0; for (int i = 1; i <= m + n; i++) { if (t == n - 1) break; if (t == n - 2) { if (join(e[i].x, e[i].y)) { if (f == 0 && e[i].x == 0 && vis[e[i].y] == 1) // f == 0 还没有添加 0 点 sum += e[i].w, t++; if (f == 1 && vis[e[i].x] == 1 && vis[e[i].y] == 1) // f == 1 已经添加 0 点 sum += e[i].w, t++; } } if (t != n - 2) if (join(e[i].x, e[i].y)) { vis[e[i].x] = vis[e[i].y] = 1; sum += e[i].w; t++; if (e[i].x == 0) f = 1; } } printf("%d ", sum); system("pause"); return 0; }
============= ========= ======== ======= ======= ====== ==== === == =
题鹤林寺僧舍 (唐 李涉 )
- 终日昏昏醉梦间,忽闻春尽强登山。
因过竹院逢僧话,偷得浮生半日闲。