题意:给你一个n*n的矩阵,让你给矩阵的每一行每一列附一个值,使得每一个矩阵中的点的行和列的值的和大于等于该点的权值,并使得行列值之和最小。
思路:其实这道题我们就是跑一遍KM算法就行了,因为在KM过程中我们始终有两点之间的期望值之和大于等于两点之间的距离,我们就可以把期望值当做行列的值就行了,最终总和就是匹配完的图的所有行列的大小总和。
代码:
1 #include2 #include 3 #include 4 using namespace std; 5 const int N=505; 6 const int Inf=0x3f3f3f3f; 7 int val[N][N],q_heng[N],q_shu[N],vis_heng[N],vis_shu[N]; 8 int match[N],slack[N],n; 9 bool dfs(int heng){ 10 vis_heng[heng]=1; 11 for(int shu=1;shu<=n;++shu){ 12 if(vis_shu[shu]) continue; 13 int gap=q_heng[heng]+q_shu[shu]-val[heng][shu]; 14 if(gap==0){ 15 vis_shu[shu]=true; 16 if(match[shu]==-1||dfs(match[shu])){ 17 match[shu]=heng; 18 return true; 19 } 20 } 21 else{ 22 slack[shu]=min(slack[shu],gap); 23 } 24 } 25 return false; 26 } 27 void KM(){ 28 memset(match,-1,sizeof(match)); 29 memset(q_shu,0,sizeof(q_shu)); 30 for(int i=1;i<=n;++i){ 31 q_heng[i]=val[i][1]; 32 for(int j=2;j<=n;++j){ 33 q_heng[i]=max(q_heng[i],val[i][j]); 34 } 35 } 36 for(int i=1;i<=n;++i){ 37 memset(slack,0x3f,sizeof(slack)); 38 while(true){ 39 memset(vis_heng,0,sizeof(vis_heng)); 40 memset(vis_shu,0,sizeof(vis_shu)); 41 if(dfs(i)) break; 42 int d=Inf; 43 for(int j=1;j<=n;++j) 44 if(!vis_shu[j]) d=min(d,slack[j]); 45 for(int j=1;j<=n;++j){ 46 if(vis_heng[j]) q_heng[j]-=d; 47 if(vis_shu[j]) q_shu[j]+=d; 48 else slack[j]-=d; 49 } 50 } 51 } 52 } 53 int main(){ 54 while(scanf("%d",&n)==1){ 55 for(int i=1;i<=n;++i) 56 for(int j=1;j<=n;++j) 57 scanf("%d",&val[i][j]); 58 KM(); 59 int ans=0; 60 for(int i=1;i<=n;++i){ 61 printf("%d ",q_heng[i]); 62 ans+=q_heng[i]; 63 } 64 printf("\n"); 65 for(int i=1;i<=n;++i){ 66 printf("%d ",q_shu[i]); 67 ans+=q_shu[i]; 68 } 69 printf("\n%d\n",ans); 70 } 71 return 0; 72 }