BZOJ 3571 画框

KM+最小乘积生成树

#include<cstdio>
#include<algorithm>
#define INF 0x7fffffff
#define NEG_INF 0x80000000
using namespace std;
const int M=75;
int A[M][M],B[M][M],W[M][M];
int left[M],Lx[M],Ly[M],slack[M],n;
bool S[M],T[M];
bool match(int x){
    S[x]=1;
    for(int i=1;i<=n;++i){
        if(T[i])continue;
        int delta=Lx[x]+Ly[i]-W[x][i];
        if(!delta){
            T[i]=1;
            if(!left[i]||match(left[i])){
                left[i]=x;
                return true;
            }
        }
        else slack[i]=min(slack[i],delta);
    }
    return false;
}
void update(){
    int delta=INF;
    for(int i=1;i<=n;i++)
        if(!T[i])delta=min(delta,slack[i]); 
    for(int i=1;i<=n;i++){
        if(S[i])Lx[i]-=delta;
        if(T[i])Ly[i]+=delta;
        else slack[i]-=delta;
    }
}
void KM(){
    for(int i=1;i<=n;i++){
        Lx[i]=NEG_INF;Ly[i]=left[i]=0;
        for(int j=1;j<=n;j++)
            Lx[i]=max(Lx[i],W[i][j]);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)slack[j]=INF;
        for(;;){
            for(int j=1;j<=n;j++)
                S[j]=T[j]=0;
            if(match(i))break;
            else update();
        }
    }
}
struct Point{int x,y;};
int ans;
void merge(Point a,Point b){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            W[i][j]=(b.y-a.y)*A[i][j]+(a.x-b.x)*B[i][j];
    KM();
    Point c;
    c.x=c.y=0;
    for(int i=1;i<=n;i++)
        c.x+=A[left[i]][i],
        c.y+=B[left[i]][i];
//  printf("Point %d %d\n",c.x,c.y);
    if((c.x==a.x&&c.y==a.y)||(c.x==b.x&&c.y==b.y))return;
    ans=min(ans,c.x*c.y);
    merge(a,c);merge(c,b);
}
void solve(){
    Point a,b;ans=INF;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            W[i][j]=-A[i][j];
    a.x=a.y=0;KM();
    for(int i=1;i<=n;++i)
        a.x+=A[left[i]][i],
        a.y+=B[left[i]][i];
    ans=min(ans,a.x*a.y);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            W[i][j]=-B[i][j];
    b.x=b.y=0;KM();
    for(int i=1;i<=n;++i)
        b.x+=A[left[i]][i],
        b.y+=B[left[i]][i];
//  printf("A %d %d\n",a.x,a.y);
//  printf("B %d %d\n",b.x,b.y);
    ans=min(ans,b.x*b.y);
    merge(a,b);
    printf("%d\n",ans);
}
int main(){
    int Cas;
    scanf("%d",&Cas);
    while(Cas--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&A[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&B[i][j]);
        solve();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值