E - Engaging with Loyal Customers Kattis - engaging (KM)(KM模板)

题目链接:

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\n",tmp.first);
120     printf("%d\n",tmp.second);
121     int len=ans.size();
122     for(int i=0; i<len; i++)
123     {
124         if(flag)// 如果交换的话,应该反着输出
125             printf("%d %d\n",ans[i].second,ans[i].first);
126         else
127             printf("%d %d\n",ans[i].first,ans[i].second);
128     }
129     return 0;
130 }

 

posted @ 2019-06-19 16:41  Let_Life_Stop  阅读(513)  评论(0编辑  收藏  举报