BZOJ3571 & 洛谷3236:[HNOI2014]画框——题解
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/+
+++++++++++++++++++++++++++++++++++++++++++