start:

2021-08-04

16:56:50

题目链接:

http://poj.org/problem?id=3723

题目内容:

Description

Windy has a country, and he wants to build an army to protect his country. He has picked up N girls and M boys and wants to collect them to be his soldiers. To collect a soldier without any privilege, he must pay 10000 RMB. There are some relationships between girls and boys and Windy can use these relationships to reduce his cost. If girl x and boy y have a relationship d and one of them has been collected, Windy can collect the other one with 10000-d RMB. Now given all the relationships between girls and boys, your assignment is to find the least amount of money Windy has to pay. Notice that only one relationship can be used when collecting one soldier.

Input

The first line of input is the number of test case.
The first line of each test case contains three integers, NM and R.
Then R lines followed, each contains three integers xiyi and di.
There is a blank line before each test case.

1 ≤ NM ≤ 10000
0 ≤ R ≤ 50,000
0 ≤ xi < N
0 ≤ yi < M
0 < di < 10000

Output

For each test case output the answer in a single line.

Sample Input

2

5 5 8
4 3 6831
1 3 4583
0 0 6592
0 1 3063
3 3 4975
1 3 2049
4 2 2104
2 2 781 5 5 10
2 4 9820
3 2 6236
3 1 8864
2 4 8326
2 0 5156
2 0 1463
4 1 2439
0 4 4373
3 4 8889
2 4 3133

Sample Output

71071
54223 核心算法:Kruskal算法 题意:需要征募女兵N人,男兵M人。每征募一个人需要10000$。但是如果已经征募的人中有一些关系亲密的人,
那么可以少花一些钱。给出若干男女之间的1~9999之间的亲密关系,
征募某个人的费用是10000-(已经征募的人中和自己的亲密度的最大值)。
要求通过适当的征募顺序使得征募所有人的所需费用最小。


题目分析:

让我们设想一下这样的一个无向图:在征募某个人的时候,如果使用了a和b之间的关系,那么就连一条从a到b的边。

假设这个图中存在圈,那么无论以什么顺序征募这个圈上的所有人,都会产生矛盾。英雌可以知道这个图是一片森林。

反之,如果给了一片森林,那么就可以使用对应的关系确定征募的顺序。

因此,把人看做顶点,把关系看做边,这个问题就可以转化为求解无向图中的最大权森林问题。

最大权森林问题可以通过吧所有的边权取反后用最小生成树求解。

为了规范Kruskal算法,在这里我会写多个函数 先写好框架:

我们使用struct结构体来储存每条边的信息,p[N]是用来储存每个点的父节点的,

第一个输入第一行的t。第二个输入n,m,r,再接下来就是r个边的信息,用struct结构体储存,

并且我们在输入r条边的信息的时候,我们注意,我们每组边的信息,包括edge[i].u,edge[i].v和edge[i].cost,

这三个分别代表女兵的编号,男兵的编号以及亲密值(edge[i].v=edge[i].v+n就是这样来的),

而且本题我们使用的是 Kruskal算法,Kruskal算法是用来计算最小生成树的,

但是我们这里需要最多的亲密度来降低花费,那么我们该怎么处理呢? 我们可以这样:

将所有的亲密度取反,让 花费-亲密度==>花费+(-亲密度),所以我们可以采取这样的方式,

只要求得 (-亲密度)的最小值,我们就能求出花费的最小值。所以我们得在输入边的信息的时候将所有的边的权重全部取反。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_E=5e5+5;
const int MAX_N=1e5+5;
struct Edge{
int u,v,cost;
}edge[MAX_E];
int n,m,r; int main()
{
ios::sync_with_stdio(false);
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&r);
v=n+m;
for(int i=0;i<r;i++)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].cost);
edge[i].v=edge[i].v+n;//女标号0~N-1,男标号N~N+M-1.
edge[i].cost=-edge[i].cost;
} }
return 0;
}

  

我们处理完基本架构之后:我们需要写一个初始化函数,用来在main函数开始时初始化一些数组。

所以定义初始化函数init(int n)

void init(int n)
{
    for(int i=0;i<n;i++)p[i]=i;
}

  

这是将所有点的父节点都初始化为它本身(比如1的父节点就是1)。

接着,Kruskal算法需要先将所有的边按权重排序,所以我们得写一个比较函数cmp(const edge& a,const edge&b)


bool cmp(const Edge &a,const Edge &b)
{
return a.cost<b.cost;
}

  

写到这里,我们先将其他的函数放一放,是时候写一部分核心函数Kruskal()

在Kruskal函数中,我们先将所有边按权重排序,所以第一行就是sort();再然后,我们在初始化每个点的父节点,

所以调用init函数,然后定义一个res=0,res将作为返回值返回Kruskal()求出来的最小生成树的权和。

接下来就是Kruskal()的核心思想了,我们按照边从小到大,如果这条边的连个端点不在一个连通块内,

那么就将这条边加入进来,看代码:

int kruskal()
{
sort(edge,edge+r,cmp);
init(v);
int res=0;
for(int i=0;i<r;i++)
{
  if(same(edge[i].u,edge[i].v)==false)
  {
unite(edge[i].u,edge[i].v);
res=res+edge[i].cost;
}
}
return res;
}

  

其中的same()函数和unite()函数接下来介绍,先说明功能:

same(int a,int b)函数是判断a点和b点是否在一个连通块内,而unite(int a,int b)是将点a和点b连到一个连通块中去。

same(itn x,int y)

bool same(int x,int y){
return find(x)==find(y);
}

  

这里的find函数也是需要我们写的,这里顺便写一下

find(int x):用来寻找点x的最终的父节点(比如说开始的时候1的父节点是1,然后1和3连通,1的最终父节点变成了3)

int find(int x)
{
  if(p[x]==x)return x;
  else return p[x]=find(p[x]);
}

  

unite(int x,int y):将点x和点y连接到一个连通块内

void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return ;
p[x]=y;
}

  

好!!!
到这里我们六个函数已经全部写完了,接下来就可以拼接起来了、 完整代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_E=5e5+5;
const int MAX_N=1e5+5;
struct Edge{
int u,v,cost;
}edge[MAX_E];
int v;
int n,m,r;
int p[MAX_N]; bool cmp(const Edge &a,const Edge &b)
{
return a.cost<b.cost;
} void init(int n)
{
for(int i=0;i<n;i++)p[i]=i;
} int find(int x)
{
if(p[x]==x)return x;
else return p[x]=find(p[x]);
} void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return ;
p[x]=y;
} bool same(int x,int y){
return find(x)==find(y);
} int kruskal()
{
sort(edge,edge+r,cmp);
init(v);
int res=0;
for(int i=0;i<r;i++)
{
if(same(edge[i].u,edge[i].v)==false)
{
unite(edge[i].u,edge[i].v);
res=res+edge[i].cost;
}
}
return res;
} int main()
{
ios::sync_with_stdio(false);
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&r);
v=n+m;
for(int i=0;i<r;i++)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].cost);
edge[i].v=edge[i].v+n;
edge[i].cost=-edge[i].cost;
}
int ans=(n+m)*10000+kruskal();
printf("%d\n",ans);
}
return 0;
}

end:

2021-08-04
21:07:23

POJ3723 Conscription 题解的更多相关文章

  1. POJ3723 Conscription 【并检查集合】

    Conscription Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8071   Accepted: 2810 Desc ...

  2. POJ3723 Conscription

    http://poj.org/problem?id=3723 这题虽然简单,但是还是错了很多次. 因为这题构建的图可能是不连通的.也就是说可能有很多棵树. 所以我以前写的并查集用在这上面会出问题的. ...

  3. Conscription poj3723(最大生成树)

    Conscription Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6870   Accepted: 2361 Desc ...

  4. Conscription [POJ3723] [最小生成树]

    Description: Windy有一个国家,他想建立一个军队来保护他的国家. 他召集了N个女孩和M男孩,想把他们雇佣成为他的士兵. 要无偿雇佣士兵,必须支付10000元. 女孩和男孩之间有一些关系 ...

  5. POJ 3723 Conscription 最小生成树

    题目链接: 题目 Conscription Time Limit: 1000MS Memory Limit: 65536K 问题描述 Windy has a country, and he wants ...

  6. Conscription

    Conscription Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 131072/65536K (Java/Other) Total ...

  7. 【POJ - 3723 】Conscription(最小生成树)

    Conscription Descriptions 需要征募女兵N人,男兵M人. 每招募一个人需要花费10000美元. 如果已经招募的人中有一些关系亲密的人,那么可以少花一些钱. 给出若干男女之前的1 ...

  8. Conscription(POJ 3723)

    原题如下: Conscription Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 16584   Accepted: 57 ...

  9. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  10. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

随机推荐

  1. NSQ(8)-有赞相关改进

    如何保证消息队列的高可用(HA) NSQ 本身就是一个分布式消息队列,且支持水平扩展,无单点故障,能在无中断的情况下无缝添加集群结点. nsq用到了集群去保证整个服务的高可用,但并不能保证单个topi ...

  2. window向linux传递文件

    1.需要在window建一个ftp的server 2.在linux的指定目录下输入: curl ftp://192.168.98.90/libalgorithm.so -u "embftp: ...

  3. 记录一次vue的报错

    1.主要代码 <div class="hello-ezuikit-js" v-for="(item,index) in list"> <div ...

  4. ideavimrc 示例

    我自己的idea vim配置,用熟悉了以后还真的挺方便的 比较常用的有 ManageRecentProjects,快速切换多个project,经常会遇到同时打开多个project,来回切换方便多了 H ...

  5. 个人常用的win7快捷键

    1.Win + D – 显示桌面 2.Win+L    锁定系统 3.Win + R – 打开运行窗口 4.Win+M     最小化所有窗口      当按下后当前所有窗口全都最小化.再次按下这个组 ...

  6. SQL_SERVER 2000启动问题

    SQL Server evaluation period has expired解决办法 问题现象: 本地计算机 上的 MSSQLSERVER 服务启动后又停止了.一些服务自动停止,如果它们没有什么可 ...

  7. Ribbon负载均衡调用

    pring cloud Ribbon 是基于Netfilix Ribbon 实现一套 客户端 负载均衡工具. 简单的说, Ribbon 是 Netflix 发布开源项目. 主要是提供客户端软件负载均衡 ...

  8. 原因代码: 0x2000c 关机类型: 关机

    进程 C:\Windows\system32\silsvc.exe (XTKFSERVER2019) 由于以下原因已代表用户 NT AUTHORITY\SYSTEM 启动计算机 XTKFSERVER2 ...

  9. ABAP 选择屏幕内的组件以及使用

    选择屏幕组件 主要记录了ABAP编程中选择屏幕常用的组件 选择框 范围选择框 radio单选 check选择 单行展现 配合radio和check使用较多 下拉框 自定义下拉框 按钮 文件框 文字帮助 ...

  10. vue的双向绑定规则

    vue提供了v-model双向绑定指令,用来辅助在不操作DOM的前提下,快速读取表单的数据 <!DOCTYPE html> <html lang="en"> ...