两个知识的本质是一样的。都是每条边有k个权值(一般k为2),现在要取一个边集M使得其将所有点连通,并使每一种边权的总和的乘积最小。不同的是一个是生成树一个是匹配。

对于这一类问题,我们都可以把每种方案的x之和与y之和作为它的坐标(x,y)

要让乘积最小,那么可能的方案的坐标一定在一个下凸壳上。

首先我们求出x最小的方案的坐标,再求出y最小方案的坐标

这就是凸壳的两个端点A,B。

然后考虑分治,每次找出离直线AB最远的点C,再继续处理

要使距离最远,就是使向量AB和向量AC的叉积最大

即最大化(c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x)

即c.x*(b.y-a.y)+c.y*(a.x-b.x)       -a.x*(b.y-a.y)+a.y*(b.x-a.x)

后面的一部分是常数,不用管。

就是要使c.x*(b.y-a.y)+c.y*(a.x-b.x) 最大化

对于生成树来说就用kruscal算法,匹配则是KM算法确定c,然后对AC、CB递归做同样的过程,直到找不到一个在左下的点C为止。

最小乘积生成树算法:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define N 205
#define M 10100
#define inf 0x3f3f3f3f
struct Edge
{int u,v;int a,b;int c;void read(){scanf("%d%d%d%d",&u,&v,&a,&b);u++;v++;}
}e[M];
bool cmpa(const Edge &a,const Edge &b){return a.a<b.a;
}
bool cmpb(const Edge &a,const Edge &b){return a.b<b.b;
}
bool cmpc(const Edge &a,const Edge &b){return a.c<b.c;
}
struct Point{int x,y;void print(){printf("%d %d\n",x,y);}Point(int _x=0,int _y=0):x(_x),y(_y){}bool operator < (const Point &A) const{unsigned int p=x;p*=y;unsigned int q=A.x;q*=A.y;return p==q?x<A.x:p<q;}
}ans,now,mina,minb;
int f[N],n,m;
int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);
}
Point kruscal()
{int i,fa,fb;now=Point(0,0);for(int i=1;i<=n;i++)f[i]=i;for(int i=1;i<=m;i++){fa=find(e[i].u);fb=find(e[i].v);if(fa!=fb){f[fb]=fa;now.x+=e[i].a;now.y+=e[i].b;}}if(now<ans)ans=now;return now;
}
int xmul(const Point &A,const Point &B,const Point &C)
{return (C.y-A.y)*(B.x-A.x)-(C.x-A.x)*(B.y-A.y);}
void work(const Point &a,const Point &b)
{for(int i=1;i<=m;i++){e[i].c=e[i].b*(a.x-b.x)+e[i].a*(b.y-a.y);}sort(e+1,e+m+1,cmpc);Point c=kruscal();if(xmul(a,b,c)<=0)return;work(a,c);work(b,c);
}
int main()
{
//  freopen("test.in","r",stdin);int i,j,k;int a,b,c;ans=Point(inf,inf);scanf("%d%d",&n,&m);for(i=1;i<=m;i++)e[i].read();sort(e+1,e+m+1,cmpa),mina=kruscal();sort(e+1,e+m+1,cmpb),minb=kruscal();work(minb,mina),ans.print();return 0;
}

最小乘积匹配:

#include<iostream>#include<cstring>
#include<cstdio>
#define inf 0x7fffffff
struct poi{int x,y;}le,ri;
int lx[75],ly[75],sla[75];
int g[75][75],a[75][75],b[75][75],f[75];
int n;
bool vx[75],vy[75];
bool operator ==(poi a,poi b){return a.x==b.x&&a.y==b.y;}
using namespace std;
bool dfs(int x)
{vx[x]=true;for (int y=1; y<=n; y++){if (!vy[y]){int t=lx[x]+ly[y]-g[x][y];if (!t){vy[y]=true;if (!f[y]||dfs(f[y])){f[y]=x;return true;}}else sla[y]=min(sla[y],t);}}return false;
}
poi km()
{memset(lx,0,sizeof(lx)); memset(ly,0,sizeof(ly)); memset(f,0,sizeof(f));for (int i=1; i<=n; i++) for (int j=1; j<=n; j++)  lx[i]=max(lx[i],g[i][j]);for (int x=1; x<=n; x++){memset(sla,63,sizeof(sla));while (true){memset(vx,0,sizeof(vx)); memset(vy,0,sizeof(vy));if (dfs(x)) break;int d=inf;for (int i=1; i<=n; i++) if (!vy[i]) d=min(d,sla[i]);for (int i=1; i<=n; i++){if (vx[i]) lx[i]-=d;if (vy[i]) ly[i]+=d;}}}poi ans=(poi) {0,0};for (int i=1; i<=n; i++) ans.x+=a[f[i]][i], ans.y+=b[f[i]][i];return ans;
}
int slove(poi l,poi r)
{for(int i=1; i<=n; i++) for (int j=1; j<=n; j++) g[i][j]=a[i][j]*(r.y-l.y)+b[i][j]*(l.x-r.x);poi mid=km();if (l==mid||r==mid) return min(l.x*l.y,r.x*r.y);elsereturn min(slove(l,mid),slove(mid,r));
}
int main()
{int t;scanf("%d",&t);for (int z=1; z<=t; z++){scanf("%d",&n);for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) scanf("%d",&a[i][j]);for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) scanf("%d",&b[i][j]);for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) g[i][j]=-a[i][j];le=km();for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) g[i][j]=-b[i][j];ri=km();printf("%d\n",slove(le,ri));}
}

转载于:https://www.cnblogs.com/137033036-wjl/p/5916546.html

最小乘积生成树和最小乘积最大匹配相关推荐

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

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

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

    正题 题目链接:https://www.luogu.com.cn/problem/P5540 题目大意 给出nnn个点mmm条边边权是一个二元组(ai,bi)(a_i,b_i)(ai​,bi​),求出 ...

  3. Lightoj 1123 - Trail Maintenance(最小增量生成树)

    题目链接 https://vjudge.net/problem/LightOJ-1123 Tigers in the Sunderbans wish to travel freely among th ...

  4. 最小代价生成树Prim/Kruskal(c/c++)

    常用的求最小代价生成树的方法有两种:Prim算法和Kruskal算法,这两种算法都是贪心算法的应用, Prim算法 在一个无向带权图中求得最小生成树得思路是:从a顶点出发,将a放入u集合(表示已选), ...

  5. POJ3522Slim Span(最大边与最小边差值最小的生成树)

    感谢这篇文章 本文对其代码,进行一些解释. 这道题的题意很明了.求最大边与最小边差值最小的生成树 首先,把所有的生成树都求出来是不可能的,所以,必须用别的方法. 在学习次小生成树的过程中,知道了一个最 ...

  6. DAG的最小路径覆盖和二分图的最大匹配

    DAG的最小路径覆盖和二分图的最大匹配 DAG的最小路径覆盖是指找最小数目的互相不相交的有向路径,满足DAG的所有顶点都被覆盖. 首先给出公式:DAG的最小路径覆盖数=DAG图中的节点数-相应二分图中 ...

  7. TZOJ 5471: 数据结构实验--图的最小代价生成树

    题目描述 求带权无向图的最小代价生成树. 输入 输入数据为多组,每组数据包含多行,第一行为2个整数n,e,n为图的顶点数,e为边数,接下来是e行,每行3个整数,前两个整数是一个顶点对,代表一条边所依附 ...

  8. 图论 —— 生成树 —— 最小瓶颈生成树

    [概述] 所谓瓶颈生成树,即对于图 G 中的生成树树上最大的边权值在所有生成树中最小. 对于无向图来说,无向图的最小生成树一定是最小瓶颈生成树,但最小瓶颈生成树不一定是最小生成树. 因此,使用 Kru ...

  9. 图的绝对中心(bzoj 2180: 最小直径生成树)

    2180: 最小直径生成树 Time Limit: 10 Sec  Memory Limit: 259 MB Submit: 219  Solved: 105 [Submit][Status][Dis ...

最新文章

  1. 那位13岁就当上老板的开发者是如何炼成的?
  2. 聊聊 TCP 长连接和心跳那些事
  3. 关于Unity中的UGUI优化,你可能遇到这些问题
  4. Element UI——数字输入框解决方案
  5. when is IBASE status changed from inital to created - not answered
  6. (dijkstra算法+多权值)最短路径问题
  7. 20-40-020-安装-kafka-eagle-bin-1.3.3安装
  8. 怎么讲gis里的符号化_地信(GIS)方向考研~?测绘科学与技术
  9. OpenDaylight风头正劲,华三通信在其中大显身手
  10. Android(java)学习笔记51:ScrollView用法
  11. 【题解】二进制优化的多重背包问题
  12. Jquery 模板插件 jquery.tmpl.js 的使用方法(2):嵌套each循环,temp调用(使用预编译的模板缓存)...
  13. css 商城 两列_你需掌握的CSS知识都在这了(长文建议收藏,文末有福利)
  14. 室内定位技术的应用及室内定位技术的种类-新导智能
  15. 我喜欢邓丽君,死掉了;
  16. 三面蚂蚁金服成功拿到offer,成功收获美团,小米offer
  17. 3套CAD别墅图纸分享分析
  18. 今日学习之Javascript
  19. Android Studio连接安卓手机驱动
  20. Web Storage知识点梳理,模拟后台管理系统部分功能

热门文章

  1. 介绍下 Emacs 的包管理功能,感觉还是比较方便的
  2. Head First Design Pattern
  3. 微博运营与微博营销最易犯的20种错误,你犯了吗?
  4. 解决方法:ORA-24324 未初始化服务句柄
  5. 在windows XP运行3660路由器仿真器
  6. Android Studio查看Android源代码失败
  7. Reactjs 踏坑指南3:一些例子(未完成)
  8. Laravel教程 一:安装及环境配置
  9. 配置内存中OLTP文件组提高性能
  10. SparkSQL使用之Thrift JDBC server