• E


    题目链接:

    https://cn.vjudge.net/problem/Kattis-engaging

    题目大意:

    n个人,m个礼物,每个人对礼物有一个满意值,然后问你整个图的最大满意度?

    具体思路:

    km模板题,学到了一个用处比较大的优化。

    km的复杂度是O(n*n*m),也就是男生的个数^2 * 女生的个数。

    如果给你输入的图中n是比较大的,我们可以通过交换n和m来降低复杂度。

    注意整个图的方向变了,所以交换的话,注意匹配的输出顺序。

    AC代码:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 # define ll long long
      4 # define INF 0x3f3f3f3f
      5 # define LL_inf (1ll<<60)
      6 const int N = 2e3+100;
      7 /* KM算法:复杂度O(nx*nx*ny)
      8 * 完全二分图求最大权匹配(必须为所有boy找到对象,且boy数量必须<=girl数量)
      9 * 若求最小权匹配,可将权值取相反数,结果取相反数
     10 * 点的编号从1开始。
     11 * 以男女模型出现比较直观。
     12 */
     13 vector<pair<int,int> >ans;
     14 int  nx, ny;                  //两边的点数,x为男,y为女。
     15 int  g[N][N];                 //二分图描述,g[x][y]表示边权。
     16 int  girl[N], Lx[N], Ly[N];   //girl[i]记录i的匹配成功对象,男女的顶标
     17 int  slack[N];      //为了优化用的,连接到对应girl的松弛值。
     18 bool S[N], T[N];    //匈牙利树的节点集合,S为男,T为女。
     19 
     20 bool DFS(int x) // x一定是boy
     21 {
     22     S[x]=true;
     23     for(int i=1; i<=ny; i++) //枚举girl
     24     {
     25         if(T[i])
     26             continue;
     27         int tmp=Lx[x]+Ly[i]-g[x][i];
     28         if( tmp==0 )
     29         {
     30             T[i]=true;
     31             //为第i个girl的男对象另找女对象
     32             if(girl[i]==-1 || DFS(girl[i]))
     33             {
     34                 girl[i]=x;      //记录匹配的boy
     35                 return true;
     36             }
     37         }
     38         else if(slack[i]>tmp)   //顺便更新下slack
     39             slack[i]=tmp;
     40     }
     41     return false;
     42 }
     43 
     44 pair<int,int>  KM()
     45 {
     46     memset(girl, -1, sizeof(girl));
     47     memset(Ly, 0, sizeof(Ly));
     48     for(int i=1; i<=nx; i++) //初始化两个L数组分别为-INF和0
     49     {
     50         Lx[i] = -INF;
     51         for(int j=1; j<=ny; j++)
     52             if(g[i][j]>Lx[i])
     53                 Lx[i]=g[i][j];
     54     }
     55     for(int j=1; j<=nx; j++)     //枚举boy
     56     {
     57         for(int i=1; i<=ny; i++) //初始slack为无穷。slack只需要记录girl的。
     58             slack[i]=INF;
     59         while(true)     //无限循环,直到帮boy[j]找到对象
     60         {
     61             memset(S, 0, sizeof(S));
     62             memset(T, 0, sizeof(T));
     63             if( DFS(j) )
     64                 break;    //直接就找到对象了,搞定。
     65             int d=INF;
     66             for(int i=1; i<=ny; i++)    //根据不在匈牙利树上的girl的slack值找到最小值d
     67                 if(!T[i] && d>slack[i])
     68                     d=slack[i];
     69             for(int i=1; i<=nx; i++)     //所有匈牙利树上的boy更新lx值
     70                 if(S[i])
     71                     Lx[i]-=d;
     72             for(int i=1; i<=ny; i++)     //树上的girl加d,不在树上的girl的slack减d。
     73             {
     74                 if(T[i])
     75                     Ly[i]+=d;   //这是为了让等式仍然成立
     76                 else
     77                     slack[i]-=d;
     78             }
     79         }
     80     }
     81     int sum=0;
     82     int num=0;
     83     for(int i=1; i<=ny; i++) //累计匹配边的权和
     84         if(girl[i]>0)
     85         {
     86             sum+=g[girl[i]][i];
     87             num++;
     88             ans.push_back(make_pair(girl[i],i));
     89         }
     90     return make_pair(sum,num);
     91 }
     92 int main()
     93 {
     94     int k;
     95     int n,m;
     96     scanf("%d %d %d",&n,&m,&k);
     97     int flag=1;
     98     if(n>m) // 如果n比较大的话,交换一下
     99     {
    100         nx=m;
    101         ny=n;
    102         flag=0;
    103     }
    104     else
    105     {
    106         nx=n;
    107         ny=m;
    108     }
    109     while(k--)
    110     {
    111         int st,ed,val;
    112         scanf("%d %d %d",&st,&ed,&val);
    113         if(!flag)
    114             g[st][ed]=val;
    115         else
    116             g[ed][st]=val;
    117     }
    118     pair<int,int> tmp=KM();
    119     printf("%d
    ",tmp.first);
    120     printf("%d
    ",tmp.second);
    121     int len=ans.size();
    122     for(int i=0; i<len; i++)
    123     {
    124         if(flag)// 如果交换的话,应该反着输出
    125             printf("%d %d
    ",ans[i].second,ans[i].first);
    126         else
    127             printf("%d %d
    ",ans[i].first,ans[i].second);
    128     }
    129     return 0;
    130 }
  • 相关阅读:
    【转】JSch
    【转】JSch
    【转】class卸载、热替换和Tomcat的热部署的分析
    关于Tomcat自动加载更新class的小技巧
    MySQL中order by中关于NULL值的排序问题
    MySQL触发器使用详解
    QuartZ Cron表达式
    JDBC的URL设置allowMultiQueries的原因
    CRT:C运行库简介
    IntelliJ IDEA安装AngularJS插件
  • 原文地址:https://www.cnblogs.com/letlifestop/p/11052273.html
Copyright © 2020-2023  润新知