Uva 11383 Golden Tiger Claw

题意:

  给定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 }

 

转载于:https://www.cnblogs.com/silver-bullet/archive/2013/02/22/2922064.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值