POJ3723 Conscription 题解
题目链接:POJ3723 Conscription
题意:要招 $n$ 个女的, $m$ 个男的,原价 $10000$,如果招了关系亲密的(男女)人可以降价,求最小花费
首先,题目给出的格式 $x_i\ y_i\ d_i$ 很像图论题
我们可以把每个人看作一个结点,为了防止男女编号相同的人搞混了,我们可以让女的编号为 $1 \sim n$ ,男的编号为 $n+1 \sim m$ ,显然这题要我们选出所有的结点且花费最小
我们可以发现答案如果存在环则会产生矛盾,例如:a招了b,b招了c,c招了a,这说不通
那是最小生成树吗?不一定
我们可以发现给定的这张图不一定是连通图,而我们要求的是花费最小的树型结构(感性理解下),那么这就是最大权森林问题,同样可以用最小生成树来求解(注:边权设为 $-d_i$)
由于不一定是树,所以不需要记录连了多少边
代码如下
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
// POJ不给用万能头文件 qwq
using namespace std;
#define int long long
#define R register
#define MAXN (int)(2e4+50)
#define MAXM (int)(5e4+50)
template<typename T>inline void read(R T &k)
{
R char ch=getchar();R T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
k=x*f;
}
int Q,n,m,r;
int f[MAXN];
int find(R int x)
{return f[x]==x?x:f[x]=find(f[x]);}
inline void merge(R int u,R int v){f[find(u)]=find(v);} // 并查集
struct Edge
{
int u,v,w,next;
void clear(){u=v=w=next=0;} // 清空
bool operator<(const Edge &o)const
{return w<o.w;}
}e[MAXM];
int head[MAXN],pos=1;
void add(R int u,R int v,R int w)
{
e[pos]={u,v,w,head[u]};
head[u]=pos++;
}
int kruskal()
{
R int res=0;
sort(e+1,e+r+1);
for(R int i=1; i<=r; i++)
{
R int u=e[i].u,v=e[i].v,w=e[i].w;
if(find(u)!=find(v))
{merge(u,v);res+=w;}
}
return res;
}
void proc()
{
read(n);read(m);read(r);pos=1;
for(R int i=1; i<=n+m; i++)
f[i]=i;
for(R int i=1; i<=r; i++)
e[i].clear();
for(R int i=1,u,v,w; i<=r; i++)
{
read(u);read(v);read(w);
v+=n;++u;++v; // 题目是从0开始编号的,我不喜欢QwQ
add(u,v,-w);
}
printf("%lld\n",10000ll*(n+m)+kruskal()); // 别忘了加上每个人10000$的原价
}
signed main()
{
read(Q);
while(Q--)proc(); // 多组数据
return 0;
}