思路大概同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