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 }
View Code

 

posted @ 2016-08-05 23:06  DUXT  阅读(364)  评论(0编辑  收藏  举报