题意:
给定n*n的矩阵,每个格子有个值s[i][j],现在要求对每行和每列各分配一个值,r[i],c[i]使得对所有的格子都有s[i][j]<=r[i]+c[j]成立,并且sum{r[i]}+sum{c[i]}尽量小。
解法:
最近在做各种匹配,看到那个不等式,很容易联想到KM算法中那个lx[i]+ly[j]>=w[i][j],在KM结束之后,所有顶标之和是最小的。
想一下KM的思想,就是通过修改顶标的值来使得尽量多的边满足lx[i]+ly[j]==w[i][j],因为这样的边才会进入相等子图。而在算法结束后,所有的点都已经完成了匹配,也就是对于每对匹配点i,j,lx[i]+ly[j]==w[i][j],显然这个时候顶标之和就是最小的。如果不是最小的,肯定能够通过修改顶标值使得某对顶点顶标值满足lx[i]+ly[j]==w[i][j],这样的话相等子图中又会多一条边,但是这个时候已经全部匹配了,所以产生矛盾。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define N 510 5 using namespace std; 6 const int inf=1<<30; 7 int s[N][N],lx[N],ly[N],mat[N],slack[N],n; 8 bool vx[N],vy[N]; 9 bool dfs(int u){ 10 vx[u]=1; 11 for(int i=1;i<=n;i++){ 12 if(!vy[i]){ 13 int t=lx[u]+ly[i]-s[u][i]; 14 if(t==0){ 15 vy[i]=1; 16 if(mat[i]==-1||dfs(mat[i])){ 17 mat[i]=u; 18 return 1; 19 } 20 }else slack[i]=min(slack[i],t); 21 } 22 } 23 return 0; 24 } 25 void KM(){ 26 memset(mat,-1,sizeof(mat)); 27 memset(ly,0,sizeof(ly)); 28 for(int i=1;i<=n;i++){ 29 lx[i]=-inf; 30 for(int j=1;j<=n;j++) 31 lx[i]=max(lx[i],s[i][j]); 32 } 33 for(int i=1;i<=n;i++){ 34 for(int j=1;j<=n;j++)slack[j]=inf; 35 while(1){ 36 memset(vx,0,sizeof(vx)); 37 memset(vy,0,sizeof(vy)); 38 if(dfs(i))break; 39 int d=inf; 40 for(int j=1;j<=n;j++) 41 if(!vy[j])d=min(d,slack[j]); 42 for(int j=1;j<=n;j++) 43 if(vx[j])lx[j]-=d; 44 for(int j=1;j<=n;j++) 45 if(vy[j])ly[j]+=d; 46 } 47 } 48 int ans=0; 49 for(int i=1;i<n;i++){ 50 printf("%d ",lx[i]); 51 ans+=lx[i]; 52 } 53 printf("%d\n",lx[n]); 54 ans+=lx[n]; 55 for(int i=1;i<n;i++){ 56 printf("%d ",ly[i]); 57 ans+=ly[i]; 58 } 59 printf("%d\n",ly[n]); 60 ans+=ly[n]; 61 printf("%d\n",ans); 62 } 63 int main(){ 64 while(~scanf("%d",&n)){ 65 for(int i=1;i<=n;i++) 66 for(int j=1;j<=n;j++) 67 scanf("%d",&s[i][j]); 68 KM(); 69 } 70 return 0; 71 }