• 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
    ", ++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
    ", ++cas, ans);
    103     }
    104     return 0;
    105 }
  • 相关阅读:
    KMP字符串查找
    基数排序
    二分查找来查找旋转数组
    通过前序(后序)+中序创建树
    二维数组找最大值
    用字符串的空格替换其他字符
    桶排序
    字符串的排列
    和为S的两个数字
    整数中1出现的次数(从1到n整数中1出现的次数)
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/9291038.html
Copyright © 2020-2023  润新知