1 条题解
-
0
洛谷 P3236 题解
思路分析
我们把每一个决策(匹配) 看成一个二维平面上的点 ,其中 ,
我们要找到最小的 ,有一个显然的优化:对于两个决策 ,如果 且 那么 肯定是不优的,因此剪枝之后我们需要维护的 在二维平面上一定构成一个下凸壳
对于 最左边的点 和最右边的点 应该是显然的:就是最小化 和最小化 Y得到的两个决策
显然,在 下方可以找到一个点 并且最大化 到 的距离,那么此时的 一定在 上,那么我们就可以对 , 分别分治了
问题转化为求 ,首先考虑过 的一条直线 $(Y_A-Y_B)\times x+(X_B-X_A)\times y=X_B\times Y_A-X_A\times Y_B$
不妨记 ,原直线方程变为
接下来考虑与 平行且过某一点 的直线 ,显然我们只需要最大化 即可得到
所以我们只需要把每条边 的边权设置为 ,然后求出新二分图的最小权完美匹配即可
使用 KM 解决此问题,时间复杂度 ,其中 为维护的凸壳的总大小
代码呈现
#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN=71,INF=1e18,MAXV=3e6; int n,ans; struct point { int x,y; point() { x=0,y=0; } inline int calc(point A,point B) { return x*(A.y-B.y)+(B.x-A.x)*y; } }; struct B_graph { int a[MAXN][MAXN],b[MAXN][MAXN]; int w[MAXN][MAXN],lx[MAXN],ly[MAXN],slack[MAXN],tar[MAXN]; bool sx[MAXN],sy[MAXN]; inline void Read() { for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { scanf("%lld",&a[i][j]); } } for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { scanf("%lld",&b[i][j]); } } } inline bool find(int x) { if(sx[x]) return false; sx[x]=true; for(int y=1;y<=n;++y) { if(sy[y]) continue; int k=lx[x]+ly[y]-w[x][y]; if(!k) { sy[y]=true; if(tar[y]==-1||find(tar[y])) { tar[y]=x; return true; } } else slack[y]=min(slack[y],k); } return false; } inline point MM(int cx,int cy) { memset(tar,-1,sizeof(tar)); memset(lx,0,sizeof(lx)); memset(ly,0,sizeof(ly)); for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { w[i][j]=MAXV-(a[i][j]*cx+b[i][j]*cy); lx[i]=max(lx[i],w[i][j]); } } for(int t=1;t<=n;++t) { memset(slack,0x3f,sizeof(slack)); memset(sx,false,sizeof(sx)); memset(sy,false,sizeof(sy)); if(find(t)) continue; while(true) { int delta=INF,y=0; for(int i=1;i<=n;++i) if(!sy[i]) delta=min(delta,slack[i]); for(int i=1;i<=n;++i) { if(sx[i]) lx[i]-=delta; if(sy[i]) ly[i]+=delta; else { slack[i]-=delta; if(!slack[i]) y=i; } } if(tar[y]==-1) break; int x=tar[y]; sx[x]=true,sy[y]=true; for(int i=1;i<=n;++i) slack[i]=min(slack[i],lx[x]+ly[i]-w[x][i]); } memset(sx,false,sizeof(sx)); memset(sy,false,sizeof(sy)); find(t); } point ret; for(int i=1;i<=n;++i) { ret.x+=a[tar[i]][i]; ret.y+=b[tar[i]][i]; } return ret; } } G; inline void solve(point A,point B) { point C=G.MM(A.y-B.y,B.x-A.x); ans=min(C.x*C.y,ans); if(C.calc(A,B)>=A.calc(A,B)) return ; solve(A,C); solve(C,B); } signed main() { int T; scanf("%lld",&T); while(T--) { scanf("%lld",&n); G.Read(); point A=G.MM(1,0),B=G.MM(0,1); ans=min(A.x*A.y,B.x*B.y); solve(A,B); printf("%lld\n",ans); } return 0; }
- 1
信息
- ID
- 5236
- 时间
- 2000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者