刚开始的想法:
用数组usedman和usedwoman来检查顶点的使用情况
对边进行排序,然后检查这条边的from和to,如果from和to都没有使用,那么ans+=20000-costr
如果from使用了 那么used【to】=true,ans+=10000-cost to同理
WA了。。
</pre><pre name="code" class="cpp">#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=10010;
bool usedman[maxn];
bool usedwoman[maxn];
typedef struct {
int a,b,cost;
}edge;
edge E[5*maxn];
bool cmp(edge a,edge b){
return a.cost > b.cost;
}
int main(int argc, char const *argv[])
{
int N,M,R;
int T;
cin>>T;
while(T--){
memset(usedwoman,false,sizeof(usedwoman));
memset(usedman,false,sizeof(usedman));
cin>>N>>M>>R;
for(int i=0;i<R;i++){
scanf("%d%d%d",&E[i].a,&E[i].b,&E[i].cost);
}
sort(E,E+R,cmp);
int ans=0;
for(int i=0;i<R;i++){
int girl=E[i].a,boy=E[i].b,cost=E[i].cost;
if(!usedwoman[girl] && !usedman[boy]){
usedwoman[boy]=usedwoman[girl]=true;
ans+=20000-cost;
}
else if(!usedwoman[girl]){
usedwoman[girl]=true;
ans+=10000-cost;
}
else if(!usedman[boy]){
usedman[boy]=true;
ans+=10000-cost;
}
}
for(int i=0;i<N;i++){
if(!usedwoman[i]) ans+=10000;
}
for(int i=0;i<M;i++){
if(!usedman[i]) ans+=10000;
}
printf("%d\n",ans);
}
return 0;
}
但是实际上,这个关系可以看成是单向的,但是似乎并没有什么问题。。
网上认为这个问题实质上是最大生成树问题,如果以上算法是错的,那么只能说是因为忽略了一些情况导致结果较大。
两个小时才找到问题:
我这样做是把边看做没有方向的,实际上边应该看做有方向的,方向指向的应该是后参加的人,因为只有后参加的人才能得到优惠。
我这样做对于不同的树合并是没有差别的,同一颗树下面的叶子不可能合并。而且,不同的树也不可能合并,但是这个合并能否减少总的费用呢?
这是肯定的,因为不同的树之间的合并可以减少树根的费用。所以还是有必要用并查集来检查是不是同一颗树的(还要明确并不是只有树根连接才能得到优化,因为两个人相连的时候,把谁当做root这个问题还没有确定,那么我们相连的时候,不管谁是root,只要不是同一颗树,都能够得到优化!(比如两棵树的叶子相连,把它们的根连在一起同样可以得到优化))
最后还是转为了最大生成树问题
<pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=50010;
int pre[2*maxn];
struct edge
{
int a,b,cost;
}e[5*maxn];
int V,T,N,M,R;
bool cmp(edge a,edge b){
return a.cost > b.cost;
}
int find(int p){
return pre[p]==p?p:pre[p]=find(pre[p]);
}
bool connect(int p,int q){
return find(p)==find(q);
}
void join(int p,int q){
int rootp=find(p);
int rootq=find(q);
if(rootq!=rootp) pre[rootp]=rootq;
}
int kruscal(){
int ans=0;
for(int i=0;i<V;i++) pre[i]=i;
sort(e,e+R,cmp);
for(int i=0;i<R;i++){
int first=e[i].a,second=e[i].b;
if(!connect(first,second)){
join(first,second);
ans+=e[i].cost;
}
}
return ans;
}
int main(int argc, char const *argv[])
{
cin>>T;
while(T--){
cin>>N>>M>>R;
V=N+M;
for(int i=0;i<R;i++){
scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].cost);
e[i].b+=N;
}
printf("%d\n",(N+M)*10000-kruscal());
}
return 0;
}