我反正是绝对做不出来。
我们把每个决策的
∑x
和
∑y
看做点
(x,y)
,那么最优解只有可能是下凸壳的点,而且在
x
最小的点A和
怎么求C?
S△ABC最大
即
cross(C−A,B−A)
最大。
cross(C−A,B−A)=cross(((C.x−A.x),(C.y−A.y)),((B.x−A.x),(B.y−a.y))=(C.x−A.x)∗(B.y−a.y)−(C.y−A.y)∗(B.x−A.x)=C.x∗(B.y−a.y)−C.y∗(B.x−A.x)−A.x∗(B.y−a.y)+A.y∗(B.x−A.x)
后两项为定值,只需前两项最大即可。
那么令二分图的权值
g(i,j)=(B.y−a.y)∗A[i][j]−(B.x−A.x)∗B[i][j]
跑KM即可。
这一题的复杂度好像并不明确。。。
这里再说一下KM算法的优化,sla要在外层循环清INF,每次DFS不是相等子图中的边则需要更新,然后update记得减去。因为sla是Y’中的点与X中的点的
min(l(x)+l(y)−g(x,y))
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<sstream>
#include<climits>
#define X first
#define Y second
#define DB double
#define lc now<<1
#define rc now<<1|1
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
template<typename T>void Read(T& x)
{
x=0;int flag=0,sgn=1;char c;
while(c=getchar())
{
if(c=='-')sgn=-1;
else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
else if(flag)break;
}
x*=sgn;
}
const int MAXN=80;
int T,n,A[MAXN][MAXN],B[MAXN][MAXN],G[MAXN][MAXN];
struct KM{
bool S[MAXN],T[MAXN];
int lx[MAXN],ly[MAXN],sla[MAXN],left[MAXN];
bool match(int i)
{
S[i]=1;
for(int j=1;j<=n;j++)
if(!T[j])
{
if(lx[i]+ly[j]==G[i][j])
{
T[j]=1;
if(!left[j]||match(left[j])){
left[j]=i;
return 1;
}
}
else
sla[j]=min(sla[j],lx[i]+ly[j]-G[i][j]);
}
return 0;
}
void update()
{
int d=INF;
for(int i=1;i<=n;i++)if(!T[i])
d=min(d,sla[i]);
for(int i=1;i<=n;i++)
{
if(S[i])lx[i]-=d;
if(T[i])ly[i]+=d;
else
sla[i]-=d;
}
}
pii km()
{
pii res=MP(0,0);
memset(left,0,sizeof(left));
memset(ly,0,sizeof(ly));
memset(lx,0,sizeof(lx));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
lx[i]=max(lx[i],G[i][j]);
for(int i=1;i<=n;i++)
{
memset(sla,INF,sizeof(sla));
while(1)
{
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
if(match(i))break;
else update();
}
}
for(int i=1;i<=n;i++)
{
res.X+=A[left[i]][i];
res.Y+=B[left[i]][i];
}
return res;
}
}Graph;
void init()
{
Read(n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
Read(A[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
Read(B[i][j]);
}
void build(pii& l,pii& r)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]=(r.Y-l.Y)*A[i][j]+(l.X-r.X)*B[i][j];
}
int work(pii l,pii r)
{
build(l,r);
pii mid=Graph.km();
if(l==mid||r==mid)return min(l.X*l.Y,r.X*r.Y);
return min(work(l,mid),work(mid,r));
}
void solve()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]=-A[i][j];
pii st=Graph.km();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]=-B[i][j];
pii ed=Graph.km();
cout<<work(st,ed)<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("frame.in","r",stdin);
freopen("frame.out","w",stdout);
#endif
Read(T);
while(T--)
{
init();
solve();
}
}