ACM学习历程—POJ3565 Ants(最佳匹配KM算法)
Young naturalist Bill studies ants in school. His ants feed on plant-louses that live on apple trees. Each ant colony needs its own apple tree to feed itself.
Bill has a map with coordinates of n ant colonies and n apple trees. He knows that ants travel from their colony to their feeding places and back using chemically tagged routes. The routes cannot intersect each other or ants will get confused and get to the wrong colony or tree, thus spurring a war between colonies.
Bill would like to connect each ant colony to a single apple tree so that all n routes are non-intersecting straight lines. In this problem such connection is always possible. Your task is to write a program that finds such connection.
On this picture ant colonies are denoted by empty circles and apple trees are denoted by filled circles. One possible connection is denoted by lines.
Input
The first line of the input file contains a single integer number n (1 ≤ n ≤ 100) — the number of ant colonies and apple trees. It is followed by n lines describing n ant colonies, followed by n lines describing n apple trees. Each ant colony and apple tree is described by a pair of integer coordinates x and y (−10 000 ≤ x, y ≤ 10 000) on a Cartesian plane. All ant colonies and apple trees occupy distinct points on a plane. No three points are on the same line.
Output
Write to the output file n lines with one integer number on each line. The number written on i-th line denotes the number (from 1 to n) of the apple tree that is connected to the i-th ant colony.
Sample Input
5
-42 58
44 86
7 28
99 34
-13 -59
-47 -44
86 74
68 -75
-68 60
99 -60
Sample Output
4
2
1
5
3
题目就是求类似图中实心到空心圆的连线,一一映射,使两两线段不相交。
有一种思路就是一开始让所有点对随意连接,然后让相交的线段进行调整,这里比较好理解,比如AC与BD相交,那么AD与BC就必然不相交了。这样的话需要的调整的次数似乎不是很好计算。
但是可以肯定的是,最终状态必然是两两不相交了。
可以发现上面相交的AC与BD,必然满足AD+BC < AC+BD。这里可以用两次三角形两边之和大于第三边进行证明。这一步让点对里面边的权值和变小了。
于是考虑,逆命题:是否当边AC与BD可以减小成AD与BC时,一定是相交的?
事实证明这个命题是不一定的,但是可以发现当可以减小成AD与BC时,AD和BC一定是不相交的。否则会导致AD+BC > AC+BD。
所以只要能减小边权的和,一定能保证不相交。那么最终状态就变成了边权和最小的状态,也就是最小匹配。可以采用KM算法进行。
似乎是数据问题,不能使用边的平方进行处理。要用double保存边的大小,然后等于0的判断,改成小于eps。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <string>
#define LL long long using namespace std; const int maxN = ;
const double inf = 1e10;
int n;
struct Point
{
double x, y;
}x[maxN], y[maxN];
int nx, ny;
int link[maxN];
double lx[maxN], ly[maxN], slack[maxN], w[maxN][maxN];//link表示和y相接的x值,lx,ly为顶标,nx,ny分别为x点集y点集的个数
bool visx[maxN], visy[maxN]; inline double dis(int i, int j)
{
double ans = (x[i].x-y[j].x)*(x[i].x-y[j].x) + (x[i].y-y[j].y)*(x[i].y-y[j].y);
return -sqrt(ans);
} bool DFS(int x)
{
visx[x] = true;
for (int y = ; y <= ny; y++)
{
if (visy[y])
continue;
double t = lx[x]+ly[y]-w[x][y];
if (fabs(t) < 1e-)
{
visy[y] = true;
if (link[y] == - || DFS(link[y]))
{
link[y] = x;
return true;
}
}
else if (slack[y] > t)//不在相等子图中slack取最小的
slack[y] = t;
}
return false;
} void KM()
{
memset(link, -, sizeof(link));
memset(ly, , sizeof(ly));
for (int i = ; i <= nx; i++)//lx初始化为与它关联边中最大的
for (int j = ; j <= ny; j++)
if (j == || w[i][j] > lx[i])
lx[i] = w[i][j]; for (int x = ; x <= nx; x++)
{
for (int i = ; i <= ny; i++)
slack[i] = inf;
for (;;)
{
memset(visx, false, sizeof(visx));
memset(visy, false, sizeof(visy));
if (DFS(x))//若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
break;//若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。
//方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,
//所有在增广轨中的Y方点的标号全部加上一个常数d
double d = inf;
for (int i = ; i <= ny; i++)
if (!visy[i] && d > slack[i])
d = slack[i];
for (int i = ; i <= nx; i++)
if (visx[i])
lx[i] -= d;
for (int i = ; i <= ny; i++)//修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d
if (visy[i])
ly[i] += d;
else
slack[i] -= d;
}
} for (int i = ; i <= n; ++i)
printf("%d\n", link[i]);
} void input()
{
nx = ny = n;
for (int i = ; i <= n; ++i)
scanf("%lf%lf", &y[i].x, &y[i].y);
for (int i = ; i <= n; ++i)
scanf("%lf%lf", &x[i].x, &x[i].y);
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
w[i][j] = dis(i, j);
} int main ()
{
//freopen("test.in", "r", stdin);
while (scanf ("%d", &n) != EOF)
{
input();
KM();
}
return ;
}
ACM学习历程—POJ3565 Ants(最佳匹配KM算法)的更多相关文章
- 二分图匹配之最佳匹配——KM算法
今天也大致学了下KM算法,用于求二分图匹配的最佳匹配. 何为最佳?我们能用匈牙利算法对二分图进行最大匹配,但匹配的方式不唯一,如果我们假设每条边有权值,那么一定会存在一个最大权值的匹配情况,但对于KM ...
- hdu2255 奔小康赚大钱 二分图最佳匹配--KM算法
传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子.这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房子住 ...
- POJ3565带权匹配——km算法
题目:http://poj.org/problem?id=3565 神奇结论:当总边权最小时,任意两条边不相交! 转化为求二分图带权最小匹配. 可以用费用流做.但这里学一下km算法. https:// ...
- 二分图最佳匹配KM算法 /// 牛客暑期第五场E
题目大意: 给定n,有n间宿舍 每间4人 接下来n行 是第一年学校规定的宿舍安排 接下来n行 是第二年学生的宿舍安排意愿 求满足学生意愿的最少交换次数 input 2 1 2 3 4 5 6 7 8 ...
- 【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法)
奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- 训练指南 UVALive - 4043(二分图匹配 + KM算法)
layout: post title: 训练指南 UVALive - 4043(二分图匹配 + KM算法) author: "luowentaoaa" catalog: true ...
- 二分图最大权匹配——KM算法
前言 这东西虽然我早就学过了,但是最近才发现我以前学的是假的,心中感慨万千(雾),故作此篇. 简介 带权二分图:每条边都有权值的二分图 最大权匹配:使所选边权和最大的匹配 KM算法,全称Kuhn-Mu ...
- 二分图 最大权匹配 km算法
这个算法的本质还是不断的找增广路: KM算法的正确性基于以下定理:若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最 ...
- 二分图带权匹配 KM算法与费用流模型建立
[二分图带权匹配与最佳匹配] 什么是二分图的带权匹配?二分图的带权匹配就是求出一个匹配集合,使得集合中边的权值之和最大或最小.而二分图的最佳匹配则一定为完备匹配,在此基础上,才要求匹配的边权值之和最大 ...
随机推荐
- Windows环境下搭建SVN服务器
使用 VisualSVN Server来实现主要的 SVN功能则要比使用原始的 SVN和Apache相配合来实现源代码的 SVN管理简单的多,下面就看看详细的说明. VisualSVN Server的 ...
- oracle img 导入dmp文件
1.新建表空间 因为我们导出的数据表的表空间不一定是USERS, 假如说是:FQDB 新建表空间SQL语句 create tablespace FQDB datafile 'c:\FQDB.dbf' ...
- Python整型int、浮点float常用方法
#!/usr/bin/env python # -*- coding:utf-8 -*- # Python整型int.浮点float # abs(x) # 返回数字的绝对值,如abs(-10) 返回 ...
- selenium java 封装
1.简单介绍 1)展示如何封装selenium的api,使其符合我们的使用需求: 2)展示如何使用page object模式写selenium脚本: 3)展示如何即时查找元素,用以操作ajax页面: ...
- Redis持久化——RDB(一)
核心知识点: 1.RDB:将当前数据生成快照保存到硬盘 2.手动触发 save:会阻塞Redis服务器直到RDB完成. bgsave:执行fork创建子进程,由子进程负责RDB操作,阻塞只发生在for ...
- pinpoint改造支持查询
原架构 改造后架构
- (转载)《C#高级编程》读书笔记
C#类型的取值范围 名称 CTS类型 说明 范围 sbyte System.SByte 8位有符号的整数 -128~127(−27−27~27−127−1) short System.Int16 16 ...
- LeetCode:最少移动次数使得数组元素相等||【462】
LeetCode:最少移动次数使得数组元素相等||[462] 题目描述 给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1. 您可以假设数组的长度最 ...
- php类和对象(二)
面向对象第三大特性:多态 概念: 当父类引用指向子类实例的时候,由于子类对父类函数进行了重写,导致我们在使用该引用取调用相应方法时表现出的不同 条件: 1.必须有继承 2.子类必须对父类的方法进行重写 ...
- ELKK 日志处理
http://blog.csdn.net/u010022051/article/details/54342357 在ELKK的架构中,各个框架的角色分工如下: ElasticSearch1.7.2:数 ...