zoukankan      html  css  js  c++  java
  • [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 了!

  • 相关阅读:
    21322
    9-1
    作业五1
    作业五2
    实验9-2
    作业4函数应用
    实验九 1
    实验八 数组2 输出一张九九乘法口诀表。要求必须将乘积放入一个二维数组中,再输出该数组,程序运行效果如下
    实验八 (调试)
    实验6剩余部分
  • 原文地址:https://www.cnblogs.com/newera/p/9109232.html
Copyright © 2011-2022 走看看