【HDU 2853】 KM算法

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2853

题意:有n个公司,m个任务,每个公司做每个任务都有一个效率值,最开始每个公司都指派了一个任务,现在要你重新给每个公司分配一个任务(一个任务只能分配给一家公司),使得所有公司任务的效率值最大,并且改变的原始任务最少。

 

思路:把每条边的权值扩大k倍(k>n),然后属于原始任务的边权值+1,权值加1是为了当两条边权值相同时,更优先选择属于原始任务的边,扩大k倍的巧妙之处不仅在于KM匹配时优先选择原始边所得答案除k得到原始答案,而且结果对k求余就是保留的就是原始任务的数量。

这种题对思维要求太高了,想不到  T.T

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 using namespace std;
 6 
 7 const int maxn=505;
 8 const int oo=1e9;
 9 int lx[maxn], ly[maxn], vx[maxn], vy[maxn], match[maxn], slack[maxn];
10 int map[maxn][maxn];
11 int n, m;
12 
13 bool find(int i)
14 {
15     vx[i]=1;                      ///相等子图中X集合
16     for(int j=1; j<=m; j++)
17         if(!vy[j])
18         {
19             int t=lx[i]+ly[j]-map[i][j];
20             if(t==0)       ///当这条边在相等子图中
21             {
22                 vy[j]=1;                  ///相等子图中Y集合
23                 if(match[j]==-1||find(match[j]))
24                 {
25                     match[j]=i;
26                     return true;
27                 }
28             }
29             else slack[j]=min(slack[j],t);
30         }
31     return false;
32 }
33 
34 int KM()
35 {
36     int  ans=0;
37     memset(match,-1,sizeof(match));
38     memset(ly,0,sizeof(ly));
39     for(int i=1; i<=n; i++)
40     {
41         lx[i]=-oo;
42         for(int j=1; j<=m; j++)
43             lx[i]=max(lx[i],map[i][j]);    ///顶标lx初始化保存的是连接i节点的最大边权值
44     }
45     for(int i=1; i<=n; i++)
46     {
47         for(int j=1; j<=m; j++) slack[j]=oo;
48         while(1)
49         {
50             memset(vx,0,sizeof(vx));
51             memset(vy,0,sizeof(vy));
52             if(find(i)) break;
53             int d=oo;
54             for(int j=1; j<=m; j++)
55                 if(!vy[j]) d=min(d,slack[j]);
56             for(int j=1; j<=n; j++)
57                 if(vx[j]) lx[j]-=d;
58             for(int j=1; j<=m; j++)
59                 if(vy[j]) ly[j]+=d;
60             for(int j=1; j<=m; j++) slack[j]-=d;
61         }
62     }
63     for(int i=1; i<=m; i++)
64         if(match[i]!=-1) ans+=map[ match[i] ][i];
65     return ans;
66 }
67 
68 int main()
69 {
70     while(cin >> n >> m)
71     {
72         for(int i=0; i<=n; i++)
73             for(int j=0; j<=m; j++) map[i][j]=-oo;
74         for(int i=1; i<=n; i++)
75             for(int j=1; j<=m; j++)
76             {
77                  int c;
78                  scanf("%d",&c);
79                  map[i][j]=max(map[i][j],100*c);
80             }
81         int sum=0;
82         for(int i=1,j; i<=n; i++)
83         {
84             scanf("%d",&j);
85             sum+=map[i][j]/100;
86             map[i][j]+=1;
87         }
88         int ans=KM();
89         printf("%d %d\n",n-ans%100,ans/100-sum);
90     }
91     return 0;
92 }
View Code

 

 

转载于:https://www.cnblogs.com/kane0526/p/3263248.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值