HDU 2426 Interesting Housing Problem(二分图最佳匹配)

http://acm.hdu.edu.cn/showproblem.php?pid=2426

题意:
每n个学生和m个房间,现在要为每个学生安排一个房间居住,每个学生对于一些房间有一些满意度,如果满意度为负就说明该学生不喜欢住在这房间。现在问如何安排可以使所有学生的满意度总和最大。(不能将学生安排到他不喜欢的房间或者他没有评价的房间)

 

思路:

二分图的最佳匹配,注意题目的要求即可。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 using namespace std;
  5 const int maxn = 500+5;
  6 const int INF = 0x3f3f3f3f;
  7 
  8 int n, m, k;
  9 int g[maxn][maxn];
 10 int lx[maxn], ly[maxn];
 11 bool S[maxn], T[maxn];  //记录左右两边节点是否已匹配
 12 int match[maxn];
 13 int slack[maxn];   //差值
 14 
 15 bool dfs(int i)
 16 {
 17     S[i] = true;
 18     for (int j = 1; j <= m; ++j)
 19     {
 20         if (T[j]) continue;   //每一轮匹配,右边节点只能一次
 21         int gap = lx[i] + ly[j] - g[i][j];
 22         if (gap == 0)   //如果符合要求
 23         {
 24             T[j] = true;
 25             if (match[j] == -1 || dfs( match[j] ))
 26             {
 27                 match[j] = i;
 28                 return true;
 29             }
 30         }
 31         else slack[j] = min(slack[j], gap);
 32     }
 33     return false;
 34 }
 35 
 36 int KM()
 37 {
 38     memset(match, -1, sizeof match);
 39     memset(ly, 0, sizeof ly);   //初始化ly为0
 40     for (int i = 1; i<=n; i++)  //初始化lx[i]为该节点所有边中的最大权值
 41     {
 42         lx[i] = g[i][1];
 43         for (int j = 2; j<=m; j++)
 44             lx[i] = max(lx[i], g[i][j]);
 45     }
 46     for (int i=1; i<=n; i++)    //尝试为每个节点匹配节点
 47     {
 48         memset(slack,INF,sizeof(slack));    //因为要取最小值,初始化为无穷大
 49         while (true) {
 50             memset(S, false, sizeof S);   //记录每轮匹配中左右节点是否被尝试匹配过
 51             memset(T, false, sizeof T);
 52             if (dfs(i)) break;
 53             int d = INF;
 54             for (int j=1; j<=m; j++)
 55                 if (!T[j]) d = min(d, slack[j]);
 56             for (int j=1; j<=n; ++j)
 57                 if (S[j]) lx[j] -= d;
 58             for(int j=1;j<=m;j++)
 59             {
 60                 if (T[j]) ly[j] += d;
 61                 else slack[j] -= d;
 62             }
 63         }
 64     }
 65 
 66     int result = 0,flag=0;
 67     for(int i = 1; i <=m; i++){
 68         if(match[i]==-1||g[match[i]][i]==-INF)
 69             continue;
 70       if(match[i]>-1){
 71         result += g[match[i]][i];
 72         flag++;
 73       }
 74     }
 75     if(flag<n) result=-1;
 76     return result;
 77 
 78 }
 79 
 80 int main()
 81 {
 82     //freopen("in.txt","r",stdin);
 83     int cas = 0;
 84     while(~scanf("%d%d%d",&n,&m,&k))
 85     {
 86         if(n>m)
 87         {
 88             printf("Case %d: %d\n", ++cas, -1);
 89             continue;
 90         }
 91         for(int i=1;i<=n;i++)
 92         for(int j=1;j<=m;j++)
 93             g[i][j] = -INF;
 94         for(int i=0;i<k;i++)
 95         {
 96             int u,v,w;
 97             scanf("%d%d%d",&u,&v,&w);
 98             u++; v++;
 99             if(w>=0) g[u][v] = w;
100         }
101         int ans = KM();
102         printf("Case %d: %d\n", ++cas, ans);
103     }
104     return 0;
105 }

 

转载于:https://www.cnblogs.com/zyb993963526/p/9291038.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值