POJ 3723 Conscription (最小生成树)

题目链接

题目大意:

在这里插入图片描述

解题思路:

本题最大干扰是男女配对,其实这个条件一点用都没有,不要想复杂了(想成了二分图,我一开始就是这么想的。。。。)

题目要求招募n个女兵,m个男兵;但是一共就n个女兵,m个男兵,也就是说全都招募了

解法很明显,让(10000 - di)求和最小,也就是边权di求和最大,就是“最大生成树“;

那么就来实现一下最大生成树算法就行!

最大生成树算法(其实就是最小生成树的变种)

把表达式变形一下就是 10000 * (n + m) + (-d1 - d2 - d3 - d4 - d5…),那么我们只需要求得(-d1 - d2 - d3 - d4…)的最小值即可

我们可以把边权di取反,再基于最小生成树求得的“最小权值和”,就是上述括号里面的最小值

因此,其实本题就是考最小生成树的模板,唯一的不同就是把边权取反

代码(最小生成树模块)

#include<bits/stdc++.h>  
using namespace std;

typedef long long LL;
typedef vector<int> VI;
typedef pair<int, int>PII;
typedef map<int, int>MII;
const int N = 500010, M = N * 2, MOD = 1e4, INF = 0x3f3f3f3f;
const double EPS = 1e-6, PI = acos(-1);

//存储每一条边
struct node{
	int u, v, w;      
	bool operator < (const node &temp) const{       //按边权升序
		return w < temp.w;
	}
}L[N];

//并查集基操
int fa[N];
void init(){
	//下标从[0, n + m - 1],原因见下面的注释(重点2)
	for(int i = 0; i < n + m; i++) fa[i] = i;
}
int findfa(int x){
	return x == fa[x] ? x : fa[x] = findfa(fa[x]);
}

int n, m, R;

int main(){
	int T;

	scanf("%d", &T);
	while(T--){
		scanf("%d%d%d", &n, &m, &R);
		//因为是多组输入,因此fa数组每次都要先初始化
		init();

		for(int i = 0; i < R; i++){
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			//重点1:边权取反;
			//重点2:因为女兵编号是[0, n - 1],男兵编号[0, m - 1],会有重复,因此让男兵编号都加上n,转换到 [n, n + m - 1],这也是init函数中初始化的下标是从[0, n + m)的原因
			L[i] = {u, v + n, -w};
		}
		
		//排序
		sort(L, L + R);
		int cnt = 0;
		int sum = 0;
		
		//最小生成树模板
		for(int i = 0; i < R; i++){
			int u = L[i].u, v = L[i].v, w = L[i].w;
			int fx = findfa(u), fy = findfa(v);
			if(fx != fy){
				fa[fx] = fy;
				sum += w;
				//因为一共有 n + m 个人,所以选中了n + m - 1条边之后就可以结束了
				if(++cnt == n + m - 1) break;
			}
		}
		//输出总价格,注意sum是负数,因此是 +sum
		printf("%d\n", (n + m) * 10000 + sum);
	}	
	
	return 0;
}	

总结

其实本题就是考察最小生成树的模板运用
难点在于:

  1. 能否辨认出二分图是干扰条件,不要陷入
  2. 能否想到用我们熟悉的最小生成树算法实现“最大生成树”

如果觉得对你有帮助,不妨点个👍吧(逃~~~)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值