题目链接:
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 }