[BZOJ 3571] 画框

 

Link:

BZOJ 3571 传送门

 

Solution:

和 BZOJ2395 的建模完全相同,(BZOJ2395 题解传送门

仅仅是将其中的基础问题由最小生成树改成了二分图最大完美匹配

只要将原来的Kruscal模块改为KM算法即可

 

Code:

//by NewErA
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int INF=1<<27;

struct Vector
{
    int x,y;
    Vector(const int &A,const int &B){x=A,y=B;}Vector(){}
};
Vector operator - (const Vector &a,const Vector &b){return Vector(a.x-b.x,a.y-b.y);}
int Cross(const Vector &a,const Vector &b){return a.x*b.y-a.y*b.x;}

const int MAXN=100;
int T,n,G[MAXN][MAXN],dat1[MAXN][MAXN],dat2[MAXN][MAXN];
Vector mina,minb,ans;
ll res;

int match[MAXN*2],slack[MAXN*2],lx[MAXN],ly[MAXN];
bool visx[MAXN],visy[MAXN];

bool dfs(int u)
{
    visx[u]=true;
    for(int i=1;i<=n;i++)
    {
        if(visy[i]) continue;
        int d=lx[u]+ly[i]-G[u][i];
        if(!d)
        {
            visy[i]=true;
            if(match[i]==-1 || dfs(match[i]))
            {
                match[i]=u;
                return true;
            }
        }
        else slack[i]=min(d,slack[i]);
    }
    return false;
}

Vector KM()  //$O(n^3)$的KM做法
{
    memset(match,-1,sizeof(match));
    fill(lx,lx+MAXN,-INF);memset(ly,0,sizeof(ly));
    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++)
    {
        fill(slack,slack+MAXN,INF);
        while(true)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfs(i)) break;
            else
            {
                int d=INF;
                for(int i=1;i<=n;i++) 
                    if(!visy[i]) d=min(d,slack[i]);
                for(int i=1;i<=n;i++)
                {
                    if(visx[i]) lx[i]-=d;
                    if(visy[i]) ly[i]+=d;
                    else slack[i]-=d;
                }
            }
        }
    }
    
    Vector cur=Vector(0,0);
    for(int i=1;i<=n;i++) 
        cur.x+=dat1[match[i]][i],cur.y+=dat2[match[i]][i];
    
    if(res>(ll)cur.x*(ll)cur.y) res=(ll)cur.x*(ll)cur.y; //注意开long long 
    return cur;
}

void Solve(Vector A,Vector B)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            G[i][j]=-(dat1[i][j]*(A.y-B.y)+dat2[i][j]*(B.x-A.x)); //修改权值
    Vector C=KM();
    if(Cross(B-A,C-A)>=0) return;
    Solve(A,C);Solve(C,B);
}

int main()
{
    cin >> T;
    while(T--)
    {
        cin >> n;res=40000*40000;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                cin >> dat1[i][j],G[i][j]=-dat1[i][j]; //最小匹配转为最大匹配,权值由正变为负即可
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                cin >> dat2[i][j];
        mina=KM();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                G[i][j]=-dat2[i][j];
        minb=KM();
        Solve(mina,minb);
        cout << res << endl;
    }
    return 0;
}

 

Review:

1、需要注意的就是最小匹配转为最大匹配的手法:

将原来的正权值求最小变为负权值求最大

 

2、千万不能再忘开long long 了!

posted @ 2018-05-30 09:44  NewErA  阅读(184)  评论(0编辑  收藏  举报