今天考试的时候果然题目太难于是我就放弃了……转而学习了一下最小乘积生成树。

最小乘积生成树定义:

(摘自网上一篇博文)。

我们主要解决的问题就是当k = 2时,如何获得最小的权值乘积。我们注意到一张图可以有很多棵生成树,我们将每一棵生成树的权值记为(x, y),表示第一种权值之和为x, 第二种权值之和为y. 这样,很自然联想到二维平面上的坐标,每一棵生成树即为这个平面上的一个点。我们所想要寻找的点就是x * y最小的点。这样的点在什么位置?显然,若x1 <= x2, y1 <= y2,1号点的权值必然更小。所以我们的答案只可能处于这张平面图上的凸包的下凸壳上。

于是我们找到A,B两点,一个离y轴最近,一个离x轴最近,这两个点一定是下凸壳的两个端点。之后,我们再寻找到与AB距离最远的点C,用点C 更新答案后再以AC,BC为新的边向下递归求解。此时问题来了:如何找到这一个距离最远,且在AB下方的C点呢?我们将距离转化为面积,使用叉积求解。因为要求C点在AB下方,所以得到的叉积必为负数。又因为|叉积| = 四边形面积,所以得到的叉积必然是负的值中绝对值最大的那一个,即求解出与AB构成的叉积最小的C点。

然后就开始考虑式子的转化:min (B - A) * (C - A) = (B.x - A.x) (C.x - A.x) - (C.x - A.x) (B.y - A.y); 化开这个式子,省去常数部分,我们发现所求就是(A.y - B.y)* a[i][j] - (A.x - B.x)* b[i][j] 最小。我们考虑将这个东西看做权值,就可以用Kruskal求出使这个值最小的C点了。如果是匹配的话,则将i --> j 视作匹配的权值,将权值取反(因为要求求最小)后跑KM算法获得最大权值匹配。

下面的代码是仿照着的,但觉得写的很漂亮,放在这里大家可以参考一下。感谢原本的博主~

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000
#define INF 99999999
int n, ans = INF, lx[maxn], ly[maxn], s[maxn], match[maxn];
int T, g[maxn][maxn], a[maxn][maxn], b[maxn][maxn];
bool visx[maxn], visy[maxn]; struct vec
{
int x, y;
}; vec operator -(vec a, vec b)
{
return (vec) { b.x - a.x, b.y - a.y };
} int operator *(vec a, vec b)
{
return a.x * b.y - a.y * b.x;
} int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} struct Graph
{
void build(int wx, int wy)
{
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
g[i][j] = - (wx * a[i][j] + wy * b[i][j]);
} bool dfs(int u)
{
visx[u] = ;
for(int v = ; v <= n; v ++)
{
if(visy[v]) continue;
int tem = lx[u] + ly[v] - g[u][v];
if(!tem)
{
visy[v] = ;
if(!match[v] || dfs(match[v]))
{
match[v] = u;
return ;
}
}
else s[v] = min(s[v], tem);
}
return false;
} vec KM()
{
memset(lx, , sizeof(lx)), memset(ly, , sizeof(ly));
memset(match, , sizeof(match));
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
lx[i] = max(lx[i], g[i][j]);
for(int i = ; i <= n; i ++)
{
memset(s, , sizeof(s));
while()
{
memset(visx, , sizeof(visx)), memset(visy, , sizeof(visy));
if(dfs(i)) break;
int tem = INF;
for(int j = ; j <= n; j ++)
if(!visy[j]) tem = min(tem, s[j]);
for(int j = ; j <= n; j ++)
if(visx[j]) lx[j] -= tem;
for(int j = ; j <= n; j ++)
if(visy[j]) ly[j] += tem;
else s[j] -= tem;
}
}
vec re; re.x = , re.y = ;
for(int i = ; i <= n; i ++)
re.x += a[match[i]][i], re.y += b[match[i]][i];
return re;
}
}G; void Solve(vec A, vec B)
{
G.build(A.y - B.y, B.x - A.x);
vec C = G.KM();
ans = min(ans, C.x * C.y);
if((A - B) * (A - C) >= ) return;
Solve(A, C), Solve(C, B);
} int main()
{
T = read();
while(T --)
{
n = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
a[i][j] = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
b[i][j] = read();
G.build(, );
vec A = G.KM();
G.build(, );
vec B = G.KM();
ans = min(A.x * A.y, B.x * B.y);
Solve(A, B);
printf("%d\n", ans);
}
return ;
}

【算法】最小乘积生成树 & 最小乘积匹配 (HNOI2014画框)的更多相关文章

  1. 树(最小乘积生成树,克鲁斯卡尔算法):BOI timeismoney

    The NetLine company wants to offer broadband internet to N towns. For this, it suffices to construct ...

  2. Bzoj2395: [Balkan 2011]Timeismoney(最小乘积生成树)

    问题描述 每条边两个权值 \(x,y\),求一棵 \((\sum x) \times (\sum y)\) 最小的生成树 Sol 把每一棵生成树的权值 \(\sum x\) 和 \(\sum y\) ...

  3. bzoj2395[Balkan 2011]Timeismoney最小乘积生成树

    所谓最小乘积生成树,即对于一个无向连通图的每一条边均有两个权值xi,yi,在图中找一颗生成树,使得Σxi*Σyi取最小值. 直接处理问题较为棘手,但每条边的权值可以描述为一个二元组(xi,yi),这也 ...

  4. HDU5697 刷题计划 dp+最小乘积生成树

    分析:就是不断递归寻找靠近边界的最优解 学习博客(必须先看这个): 1:http://www.cnblogs.com/autsky-jadek/p/3959446.html 2:http://blog ...

  5. 【BZOJ2395】【Balkan 2011】Timeismoney 最小乘积生成树

    链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网 ...

  6. Luogu5540 最小乘积生成树

    Luogu5540 最小乘积生成树 题目链接:洛谷 题目描述:对于一个\(n\)个点\(m\)条边的无向连通图,每条边有两个边权\(a_i,b_i\),求使\((\sum a_i)\times (\s ...

  7. P5540-[BalkanOI2011]timeismoney|最小乘积生成树【最小生成树,凸壳】

    正题 题目链接:https://www.luogu.com.cn/problem/P5540 题目大意 给出\(n\)个点\(m\)条边边权是一个二元组\((a_i,b_i)\),求出一棵生成树最小化 ...

  8. bzoj2395 [Balkan 2011]Timeismoney(最小乘积生成树+计算几何)

    题意 每条边有两个权值\(c,t\),请求出一颗生成树,使得\(\sum c\times \sum t\)最小 题解 为什么生成树会和计算几何扯上关系-- 对于每棵树,设\(x=c,y=t\),我们可 ...

  9. BZOJ2395 [Balkan 2011]Timeismoney 【最小乘积生成树】

    题目链接 BZOJ2395 题意:无向图中每条边有两种权值,定义一个生成树的权值为两种权值各自的和的积 求权值最小的生成树 题解 如果我们将一个生成树的权值看做坐标,那么每一个生成树就对应一个二维平面 ...

随机推荐

  1. 三角形div原理(小知识点)

    三角形div其实就是从边框的演变过程 #sider2{ width: 100px; height: 100px; border-top: 30px solid #000; border-right:  ...

  2. APSchedule的练习使用

    1 简介 APScheduler的全称是Advanced Python Scheduler.它是一个轻量级的 Python 定时任务调度框架.APScheduler 支持三种调度任务:固定时间间隔,固 ...

  3. PHP学习课程和培训方向学习路线分享

    php语言的优越性,集结了很多的开发爱好者,无论行业前景和个人发展来说,php正飞速的发展,php在不断兼容着类似closures和命名空间 等技术,同时兼顾性能和当下流行的框架.版本是7之后,一直在 ...

  4. 基于pyecharts的IT各行业薪资展示

    我们的项目是一个信息采集系统,采集的是51job招聘网站,我爬取了Python,Java,C++,PHP还有北京各地区的职位数量,以及经验要求,和学历要求等等. 网页头; <!DOCTYPE h ...

  5. UVA - 1606 Amphiphilic Carbon Molecules 极角扫描法

    题目:点击查看题目 思路:这道题的解决思路是极角扫描法.极角扫描法的思想主要是先选择一个点作为基准点,然后求出各点对于该点的相对坐标,同时求出该坐标系下的极角,按照极角对点进行排序.然后选取点与基准点 ...

  6. 《UML大战需求分析》阅读笔记1

    通过阅读本书的序和第一章,让我对于UML的理解更加深刻了,并且懂了怎样把你UML学的更好. 作者先让我们明白什么是UML,大概知道了UML各个图的形态和各种用途,然后再详细的介绍各个图怎样使用. UM ...

  7. 线上环境HBASE-1.2.0出现oldWALs无法自动回收情况;

    正常情况下,hmaster会定期清理oldWALs文件夹,一般该文件大小也就几百兆,但是我们线上 环境出现了该文件没有自动回收情况,如图: 该目录占用hdfs空间多达7.6T,浪费空间: 后来经过多番 ...

  8. javascript代码规范 [转]

    原文:http://www.css88.com/archives/5366 全局命名空间污染与 IIFE 总是将代码包裹成一个 IIFE(Immediately-Invoked Function Ex ...

  9. stm32--USB(作为U盘)+FatFs的实现

    一.USB功能的添加(作为U盘) 添加文件 将官方库中的Library文件夹中的所有有效文件添加到工程中,分为4个文件夹: usb class为硬件相关(Library\Class): usb dri ...

  10. java网络编程框架

    虽然写过一些网络编程方面的东西,但还没有深入研究过这方面的内容,直接摘录一些文章,后续整理 原文地址:http://blog.csdn.net/lwuit/article/details/730613 ...