bzoj3571: [Hnoi2014]画框 最小乘积匹配+最小乘积XX总结,

思路大概同bzoj2395(传送门:http://www.cnblogs.com/DUXT/p/5739864.html),还是将每一种匹配方案的Σai看成x,Σbi看成y,然后将每种方案转化为平面上的点,再用km去找最远的点就行了。

然而几个月前就学过km且到现在还未写过一道km的题的我并不知道km如何对于负权给出最优解。。。。

#define XX 某传统算法(例如:最小生成树,二分图最优带权匹配什么的)

顺便总结一下最小乘积XX

即对于XX引入两个权值的概念(或是多个权值,一般是两个),看似无从下手,却可以将每一组可行解的方案的两个sum转化为平面内一个点,然后就可以发现一个十分优美的性质即最优解一定在凸包上,于是可以利用一种类似于快包算法的算法,依旧是用XX找出一定在凸包上的两个点,然后再次利用XX进行分治,递归地下去找,弄清楚边界条件(一般一个叉乘就可以轻松搞定),这样问题就被轻易地解决了。(貌似知道了这样的一个模板,基本所有类似问题都可以得到解决)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 #define maxn 100
 8 #define inf 100000000
 9
10 int cases,n;
11 int a[maxn][maxn],b[maxn][maxn],val[maxn][maxn],slack[maxn],valx[maxn],valy[maxn],linky[maxn];
12 bool visx[maxn],visy[maxn];
13
14 struct point{
15     int x,y;
16 }ans;
17
18 point operator -(point a,point b){return(point){a.x-b.x,a.y-b.y};}
19 double operator *(point a,point b){return a.x*b.y-a.y*b.x;}
20
21 bool find(int x){
22     visx[x]=1;
23     for (int y=1;y<=n;y++)
24         if (!visy[y]){
25             int t=valx[x]+valy[y]-val[x][y];
26             if (!t){
27                 visy[y]=1;
28                 if (!linky[y]||find(linky[y])){
29                     linky[y]=x;
30                     return 1;
31                 }
32             }
33             else slack[y]=min(slack[y],t);
34         }
35     return 0;
36 }
37
38 point km(){
39     memset(valx,0,sizeof(valx));
40     memset(valy,0,sizeof(valy));
41     memset(linky,0,sizeof(linky));
42     for (int i=1;i<=n;i++)
43         for (int j=1;j<=n;j++)
44             valx[i]=max(valx[i],val[i][j]);
45     for (int x=1;x<=n;x++){
46         memset(slack,63,sizeof(slack));
47         while (1){
48             memset(visx,0,sizeof(visx));
49             memset(visy,0,sizeof(visy));
50             if (find(x)) break;
51             int d=inf;
52             for (int i=1;i<=n;i++) if (!visy[i]) d=min(d,slack[i]);
53             for (int i=1;i<=n;i++) if (visx[i]) valx[i]-=d;
54             for (int i=1;i<=n;i++) if (visy[i]) valy[i]+=d;
55         }
56     }
57     point now={0,0};
58     for (int i=1;i<=n;i++) now.x+=a[linky[i]][i],now.y+=b[linky[i]][i];
59     if ((ans.x==inf&&ans.y==inf)||(ans.x*ans.y>now.x*now.y)) ans=now;
60     return now;
61 }
62
63 bool operator ==(point a,point b){return a.x==b.x&&a.y==b.y;}
64
65 void solve(point x,point y){
66     for (int i=1;i<=n;i++)
67         for (int j=1;j<=n;j++)
68             val[i][j]=b[i][j]*(x.x-y.x)+a[i][j]*(y.y-x.y);
69     point z=km();
70     if ((z-x)*(y-z)<=0) return;
71     solve(x,z);
72     solve(z,y);
73 }
74
75 int main(){
76     scanf("%d",&cases);
77     while (cases--){
78         scanf("%d",&n);
79         ans.x=ans.y=inf;
80         for (int i=1;i<=n;i++)
81             for (int j=1;j<=n;j++) scanf("%d",&a[i][j]);
82         for (int i=1;i<=n;i++)
83             for (int j=1;j<=n;j++) scanf("%d",&b[i][j]);
84         point minx,miny;
85         for (int i=1;i<=n;i++)
86             for (int j=1;j<=n;j++) val[i][j]=-a[i][j];
87         minx=km();
88         for (int i=1;i<=n;i++)
89             for (int j=1;j<=n;j++) val[i][j]=-b[i][j];
90         miny=km();
91         solve(minx,miny);
92         printf("%d\n",ans.x*ans.y);
93     }
94     return 0;
95 }

时间: 2024-10-12 12:56:21

bzoj3571: [Hnoi2014]画框 最小乘积匹配+最小乘积XX总结,的相关文章

bzoj3571[Hnoi2014]画框

http://www.lydsy.com/JudgeOnline/problem.php?id=3571 好吧,裸的最小乘积匹配 现在才会KM算法....... #include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #incl

BZOJ 3571 [Hnoi2014]画框(最小乘积完美匹配)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3571 [题目大意] 给出一张二分图,每条边上有a,b两个值,求完美匹配, 使得suma*sumb最小. [题解] 把方案看成一个二维点,x=sum(a),y=sum(b) 答案一定在下凸壳上,找到l,r两个点,l是x最小的,r是y最小的 然后递归调用work(l,r):找到离该直线最远的点,那个点一定在下凸壳上 将边权设为(a,b)叉积(l-r),求出最小完美匹配就是那个点mid 因

POJ 3686 The Windy&#39;s【最小权匹配(神建图啊)】

大意:有n个任务m个机器,告诉你n*m的矩阵表示每个任务在每个机器上完成需要的时间 问所有任务完成的总时间最少?(比如第一个任务在第一分钟完成第二个任务在第二分钟完成   则总时间为1 + 2 = 3 分析: 该题自己做的时候没有思路 后来在网上搜题解,感觉建图真是太厉害了 假设最优情况下,个个任务需要的时间分别为a1, a2, a3, ……,an 那么总时间t = n * a1 + (n - 1) * a2 + ……+ 2 * an - 1 + an 也就是说只需要考虑系数就可以了 我们先假设

poj3565Ants【最小权匹配】

大意: 左图为一个坐标轴上的点  其中黑点代表ants 白点代表apple 问怎样安排ants匹配apple才能使人一两条边不想交 分析: 如左图,我们假设a->d,b->c为一个最佳匹配  交点为e 那么ad+bc = ae+ ed + be + ec = (ae + ec) + (be + ed)  该值大于ac+bd 所以匹配为最佳匹配不成立 因此我们只要求出二分图的最小匹配就是结果了 但是wa了: wa的代码: 1 #include <iostream> 2 #includ

poj 2195 Going Home 二分图最小权匹配KM算法

题意: 有n个人要回到n间房子里,每间房子只允许一个人,求n个人要走的最小距离和. 分析: 裸的二分图最小权匹配,KM搞之. 代码: //poj 2195 //sep9 #include <iostream> using namespace std; const int maxN=128; char g[maxN][maxN]; int mx[maxN],my[maxN],hx[maxN],hy[maxN]; int w[maxN][maxN]; int lx[maxN],ly[maxN],l

ural 1076 KM求最小权匹配

贴模板~ KM算法引进了顶标函数,不断缩小这个顶标来让相等子图的可能范围扩大 #include<iostream> #include<cstring> //KM 复杂度O^3 using namespace std; const int N=200; int lx[N],ly[N];//顶标函数 int w[N][N];//图 bool vix[N],viy[N]; int linky[N];// int lack;//每次顶标函数扩大的增量 int n,m; bool find(

二分图最佳匹配,求最大权匹配或最小权匹配

Beloved Sons http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1338 题意:国王有N个儿子,现在每个儿子结婚都能够获得一定的喜悦值,王子编号为1-N,有N个女孩的编号同样为1-N,每个王子心中都有心仪的女孩,现在问如果安排,能够使得题中给定的式子和最大. 分析:其实题目中那个开根号是个烟雾弹,只要关心喜悦值的平方即可.那么对王子和女孩之间构边,边权为喜悦值的平方,对于每一个王子虚拟出一个女孩边权为0,这样是为了所

POJ 2195 二分图最小权匹配KM算法

本来是打算昨天晚上写的, 昨天网速渣的连CSDN都进不去,没办法 只能现在来写了 先写写对KM算法的理解,KM算法是对每个点设置一个顶标,只有当边长等于两边点的顶标之和的时候才进行增广,这样就能保证得到的一定是最大权匹配. 如果找不到匹配的时候就对交替路中X集合的顶标减少一个d Y集合的顶标增加一个d. 这样两个点都在交替路中的时候x[i]+y[i]的和不边 X在 Y不在的时候x[i]+y[i]减少,可能就会为图增加一对匹配. X不在Y在的时候x[i]+y[i]增加, 原来不在现在依然不在其中.

HDU 3360 National Treasures 奇偶匹配最小点覆盖

题目来源:HDU 3360 National Treasures 题意:如果a[i][j] != -1 把他转成二进制 最多有12位 代表题目那张图的12个位置 如果对应位是1 说明在那里放一个守卫可以看住a[i][j]位置上的这个东西 思路:明显死最小点覆盖 奇偶匹配建图 #include <cstdio> #include <cstring> #include <vector> using namespace std; const int maxn = 55; in