题意

    有n个画框和n幅画。若第i幅画和第j个画框配对,则有平凡度Aij和违和度Bij,一种配对方案的总体不和谐度为∑Aij*∑Bij。求通过搭配能得到的最小不和谐度是多少。 n <= 70.

  分析

    这题是最小乘积最大权匹配裸题,其做法类似最小乘积生成树。

    每个方案可以表示为二维平面上的点,答案必然在下凸壳上。

    具体要怎么找呢?其实是有一个这样的方法:找出横坐标或纵坐标最小的点a和b,找点的方法可以用KM。

    找到这两个点就可以分治下去做了,找到离直线ab距离最大的点(当然要在直线ab下方)。

    列出点线距离公式,由于要找的点是在直线ab的下方,那么绝对值就可以去掉,整理为Ax+By的最值,然后就化成了一维,继续用KM来找,如此递归下去做。

    当然,最小乘积XXX的东西似乎都可以用上面的方法做,拓展到多维方法也是类似的。

  程序

 #include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream> using namespace std; const int maxn = ;
const int INF = 0x3fffffff;
bool visx[maxn], visy[maxn];
int linker[maxn], slack[maxn], lx[maxn], ly[maxn], w[maxn][maxn];
int n, a[maxn][maxn], b[maxn][maxn];
struct Point
{
int x, y;
Point (int x = , int y = ):
x(x), y(y) {}
bool operator == (const Point &AI) const
{
return AI.x == x && AI.y == y;
}
}; bool dfs(int x)
{
int y, temp;
visx[x] = true;
for (y = ; y <= n; ++y)
{
if (visy[y]) continue ;
temp = lx[x]+ly[y]-w[x][y];
if (temp == )
{
visy[y] = true;
if (linker[y] == - || dfs(linker[y]))
{
linker[y] = x;
return true;
}
}
else if (temp < slack[y]) slack[y] = temp;
}
return false;
} Point KM()
{
int i, j, x, y, d;
memset(ly, , sizeof(ly));
memset(linker, -, sizeof(linker));
for (i = ; i <= n; ++i)
{
lx[i] = -INF;
for (j = ; j <= n; ++j) lx[i] = max(lx[i], w[i][j]);
}
for (x = ; x <= n; ++x)
{
for (y = ; y <= n; ++y) slack[y] = INF;
while ()
{
memset(visx, , sizeof(visx));
memset(visy, , sizeof(visy));
if (dfs(x)) break ;
d = INF;
for (y = ; y <= n; ++y) if (!visy[y]) d = min(d, slack[y]);
for (i = ; i <= n; ++i) if (visx[i]) lx[i] -= d;
for (y = ; y <= n; ++y)
if (visy[y]) ly[y] += d;
else slack[y] -=d;
}
}
Point temp(, );
for (i = ; i <= n; ++i)
temp.x += a[linker[i]][i], temp.y += b[linker[i]][i];
return temp;
} int solve(Point p1, Point p2)
{
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
w[i][j] = (p2.y-p1.y)*a[i][j]+(p1.x-p2.x)*b[i][j];
Point t = KM();
if (t == p1 || t == p2) return min(p1.x*p1.y, p2.x*p2.y);
return min(solve(p1, t), solve(t, p2));
} int main()
{
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
int T;
scanf("%d", &T);
while (T --)
{
scanf("%d", &n);
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
scanf("%d", &a[i][j]);
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
scanf("%d", &b[i][j]);
Point p1, p2;
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
w[i][j] = -a[i][j];
p1 = KM();
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
w[i][j] = -b[i][j];
p2 = KM();
printf("%d\n", solve(p1, p2));
}
return ;
}

BZOJ 3571 画框 KM算法 最小乘积最大权匹配的更多相关文章

  1. [BZOJ 3571] 画框

    Link: BZOJ 3571 传送门 Solution: 和 BZOJ2395 的建模完全相同,(BZOJ2395 题解传送门) 仅仅是将其中的基础问题由最小生成树改成了二分图最大完美匹配 只要将原 ...

  2. hdu 3395(KM算法||最小费用最大流(第二种超级巧妙))

    Special Fish Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tota ...

  3. hdu 3488(KM算法||最小费用最大流)

    Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submis ...

  4. 运动员最佳匹配问题 KM算法:带权二分图匹配

    题面: 羽毛球队有男女运动员各n人.给定2 个n×n矩阵P和Q.P[i][j]是男运动员i和女运动员j配对组成混合双打的男运动员竞赛优势:Q[i][j]是女运动员i和男运动员j配合的女运动员竞赛优势. ...

  5. hdu 4862 KM算法 最小K路径覆盖的模型

    http://acm.hdu.edu.cn/showproblem.php?pid=4862 选t<=k次,t条路要经过全部的点一次而且只一次. 建图是问题: 我自己最初就把n*m 个点分别放入 ...

  6. bzoj 2395 [Balkan 2011]Timeismoney——最小乘积生成树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2395 如果把 \( \sum t \) 作为 x 坐标,\( \sum c \) 作为 y ...

  7. BZOJ.1497.[NOI2006]最大获利(最小割 最大权闭合子图Dinic)

    题目链接 //裸最大权闭合子图... #include<cstdio> #include<cctype> #include<algorithm> #define g ...

  8. km板子(二分图最大权匹配)

    //#pragma comment(linker, "/stack:200000000") //#pragma GCC optimize("Ofast,no-stack- ...

  9. 二分图带权匹配、最佳匹配与KM算法

    ---------------------以上转自ByVoid神牛博客,并有所省略. [二分图带权匹配与最佳匹配] 什么是二分图的带权匹配?二分图的带权匹配就是求出一个匹配集合,使得集合中边的权值之和 ...

随机推荐

  1. Linux mint 18.1 / Ubuntu 16.04 安装steam

    这里以Limit Mint 18.1为例: 安装steam: sudo dpkg -i steam.deb 运行后会有如下错误: 直接运行如下命令修复, 并自动启动steam: LD_PRELOAD= ...

  2. flask基础之jijia2模板使用基础(二)

    前言 在以前前后端不分离的时代,后台程序员往往又当爹又当妈,需要将前端程序员写的h5页面填充模板语言.而jijia2是一门十分强大的python的模板语言,是flask框架的核心模块之一.先简单介绍一 ...

  3. Linux进程的创建函数fork()及其fork内核实现解析【转】

    转自:http://www.cnblogs.com/zengyiwen/p/5755193.html 进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进 ...

  4. elk系列7之通过grok分析apache日志【转】

    preface 说道分析日志,我们知道的采集方式有2种: 通过grok在logstash的filter里面过滤匹配. logstash --> redis --> python(py脚本过 ...

  5. Excel---导出与读取(大数据量)

    Excel下载 首先大数据量的下载,一般的Excel下载操作是不可能完成的,会导致内存溢出 SXSSFWorkbook 是专门用于大数据了的导出 构造入参rowAccessWindowSize 这个参 ...

  6. python模块之cx_Oracle

    安装cx_Oracle wget http://download.oracle.com/otn/linux/instantclient/122010/instantclient-basic-linux ...

  7. java.lang.IllegalArgumentException: Page directive: invalid value for import

    我的项目原来用的tomcat版本是apache-tomcat-7.0.53,后来为了安全原因将版本升至\apache-tomcat-7.0.57,发现有的jsp页面出现下面的异常: java.lang ...

  8. plsPlugin

    init: 监控目录变化(增删) 监控jar变化,load

  9. hdu4307

    好题,详细题解在这里http://blog.csdn.net/weiguang_123/article/details/8077385 这里回顾一下: 当i和j都在一个集合里会产生新的收益,是经典题直 ...

  10. 【LOJ】#2174. 「FJOI2016」神秘数

    题解 这道题的结论很显然= = 就是暴力求的话,把一个区间的数排一下序,如果当前这个数大于前面所有数的前缀和+1,那么前缀和+1即我们所求的答案 那么我们设置一个当前答案(初始为1),在主席树上求出来 ...