[BZOJ3571][HNOI2014]画框

我反正是绝对做不出来。
我们把每个决策的 x y 看做点 (x,y) ,那么最优解只有可能是下凸壳的点,而且在 x 最小的点A和y最小的点B之间的下凸壳。我们先求出A、B,观察得在线段AB下方离线段AB最远的点C一定在下凸壳上,这样就可以分治(A,C)和(B,C)了。
怎么求C?
SABC cross(CA,BA) 最大。
cross(CA,BA)=cross(((C.xA.x),(C.yA.y)),((B.xA.x),(B.ya.y))=(C.xA.x)(B.ya.y)(C.yA.y)(B.xA.x)=C.x(B.ya.y)C.y(B.xA.x)A.x(B.ya.y)+A.y(B.xA.x)
后两项为定值,只需前两项最大即可。
那么令二分图的权值 g(i,j)=(B.ya.y)A[i][j](B.xA.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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值