1. 程式人生 > >BZOJ3571 & 洛谷3236:[HNOI2014]畫框——題解

BZOJ3571 & 洛谷3236:[HNOI2014]畫框——題解

() mat 匹配 clas new 和諧 lan blank 希望

https://www.lydsy.com/JudgeOnline/problem.php?id=3571

https://www.luogu.org/problemnew/show/P3236

小T準備在家裏擺放幾幅畫,為此他買來了N幅畫和N個畫框。為了體現他的品味,小T希望能合理地搭配畫與畫框,使得其顯得既不過於平庸也不太違和。對於第 幅畫與第 個畫框的配對,小T都給出了這個配對的平凡度Aij 與違和度Bij 。整個搭配方案的總體不和諧度為每對畫與畫框平凡度之和與每對畫與畫框違和度的乘積。具體來說,設搭配方案中第i幅畫與第Pi個畫框配對,則總體不和諧度為

技術分享圖片

小T希望知道通過搭配能得到的最小的總體不和諧度是多少。

我能說我debug 1h才發現j打成i了嗎TAT

對於最小代價二分圖匹配可以KM算法做(當然費用流是可以的但是O(玄學)對吧……)。

這個所求表達式形式像極了最小乘積生成樹:BZOJ2395:[Balkan 2011]Timeismoney

所以其實我們只是把生成樹部分變成KM就行啦!

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=102
; const int INF=0x3f3f3f3f; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==-;ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct point{ int x,y; point(int xx=0,int yy=0){x=xx,y=yy;} point
operator -(const point &b)const{ return point(x-b.x,y-b.y); } int operator *(const point &b)const{ return x*b.y-y*b.x; } }; int n,ans,a[N][N],b[N][N]; int dis[N][N],wx[N],wy[N],match[N],sla[N]; bool vx[N],vy[N]; bool dfs(int u){ vx[u]=1; for(int v=1;v<=n;v++){ if(!vy[v]){ int w=wx[u]+wy[v]-dis[u][v]; if(!w){ vy[v]=1; if(!match[v]||dfs(match[v])){ match[v]=u;return 1; } }else sla[v]=min(sla[v],w); } } return 0; } point KM(){ point res=point(0,0); memset(wx,0,sizeof(wx)); memset(wy,0,sizeof(wy)); memset(match,0,sizeof(match)); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) wx[i]=max(wx[i],dis[i][j]); for(int i=1;i<=n;i++){ memset(sla,INF,sizeof(sla)); while(1){ memset(vx,0,sizeof(vx)); memset(vy,0,sizeof(vy)); if(dfs(i))break; int minn=INF; for(int j=1;j<=n;j++) if(!vy[j])minn=min(minn,sla[j]); for(int j=1;j<=n;j++){ if(vx[j])wx[j]-=minn; if(vy[j])wy[j]+=minn; else sla[j]-=minn; } } } for(int i=1;i<=n;i++) res.x+=a[match[i]][i],res.y+=b[match[i]][i]; ans=min(ans,res.x*res.y); return res; } void work(point l,point r){ for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=-(a[i][j]*(l.y-r.y)+b[i][j]*(r.x-l.x)); point mid=KM(); if((r-l)*(mid-l)>=0)return; work(l,mid);work(mid,r); } int main(){ int t=read(); while(t--){ ans=INF;n=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) b[i][j]=read(); for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=-a[i][j]; point p1=KM(); for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=-b[i][j]; point p2=KM(); work(p1,p2); printf("%d\n",ans); } return 0; }

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+歡迎訪問我的博客:http://www.cnblogs.com/luyouqi233/ +

+++++++++++++++++++++++++++++++++++++++++++

BZOJ3571 & 洛谷3236:[HNOI2014]畫框——題解