题目大意:有 n 个老板 和 n 个员工 ,每个老板对每个员工都有一个满意度(范围 1 ~ n ),每个员工对每个老板也有一个满意度(范围1 ~ n ),但每个老板只能雇佣一个员工 , 每个员工也只能为一个老板工作,定义 :平均满意度 = ((每个人的满意度之和) - 2 * n )/ (2 * n) ,要求找出是平均满意度最小的分配方案,如果有多种方案,则按员工序号的字典序输出(即老板的编号始终是按 1 ~ n 输出,按每个老板对应员工的序号的字典序输出)。
解题思路:这道题是找出所有的最小权值匹配 ,用到KM算法和回溯找全排列(注意剪枝!),具体请看程序。
Ps:这道题的输入有问题,应先输入 第 二个 矩阵, 再输入 第 一个 矩阵 ,还有 找全排列的时候别忘剪枝!!
代码如下:
#include<iostream> #include<cstring> #include<string> #include<algorithm> #include<cmath> #include<cstdio> #include<vector> #include<queue> #define mem(a , b) memset(a , b , sizeof(a)) using namespace std ; const int INF = 0x7fffffff ; const int MAXN = 20 ; int w[MAXN][MAXN] ; int sp[MAXN][MAXN] , em[MAXN][MAXN] ; int Lx[MAXN] , Ly[MAXN] ; int link[MAXN] , linkx[MAXN] ; int slack[MAXN] ; bool visx[MAXN] , visy[MAXN] ; bool vis[MAXN] ; int flag ; int tans ; int sc ; double ans ; int ca ; int n ; void chu() { mem(link , -1) ; mem(Lx , 0) ; mem(Ly , 0) ; mem(visx , 0) ; mem(visy , 0) ; sc = 0 ; } void init() { scanf("%d" , &n) ; int i , j ; for(i = 1 ; i <= n ; i ++) { for(j = 1 ; j <= n ; j ++) { int tmp ; scanf("%d" , &tmp) ; em[i][tmp] = j ; // 员工对老板的满意度 } } for(i = 1 ; i <= n ; i ++) { for(j = 1 ; j <= n ; j ++) { int tmp ; scanf("%d" , &tmp) ; sp[i][tmp] = j ; // 老板对员工的满意度 } } for(i = 1 ; i <= n ; i ++) // 建图 { for(j = 1 ; j <= n ; j ++) { w[i][j] = (sp[i][j] + em[j][i]) * (-1) ; } } } int dfs(int u) { visx[u] = 1 ; int v ; for(v = 1 ; v <= n ; v ++) { if(visy[v]) continue ; int t = Lx[u] + Ly[v] - w[u][v] ; if(t == 0) { visy[v] = 1 ; if(link[v] == -1 || dfs(link[v])) { link[v] = u ; linkx[u] = v ; return 1 ; } } else if(slack[v] > t) { slack[v] = t ; } } return 0 ; } void KM() { int i , j ; int MAX = -INF ; for(i = 1 ; i <= n ; i ++) { for(j = 1 ; j <= n ; j ++) { if(w[i][j] > MAX) MAX = w[i][j] ; } Lx[i] = MAX ; } mem(Ly , 0) ; for(i = 1 ; i <= n ; i ++) { for(j = 1 ; j <= n ; j ++) { slack[j] = INF ; } while (1) { mem(visx , 0) ; mem(visy , 0) ; if(dfs(i)) break ; int d = INF ; int k ; for(k = 1 ; k <= n ; k ++) { if(!visy[k] && d > slack[k]) d = slack[k] ; } for(k = 1 ; k <= n ; k ++) { if(visx[k]) Lx[k] -= d ; if(visy[k]) Ly[k] += d ; else { slack[k] -= d ; } } } } tans= 0 ; for(i = 1 ; i <= n ; i ++) { tans += w[link[i]][i] ; } ans = (-1.0 * tans - 2 * n) / (2.0 * n) ; } void find(int cnt , int cost) //全排列搜索找出所有答案 { if(cost < tans) return ; // 此处剪枝很重要,不然会 TLE !! if(cnt > n) { if(tans != cost) return ; printf("Best Pairing %d\n", ++ sc); for(int i = 1 ; i <= n ; i ++) printf("Supervisor %d with Employee %d\n", i , linkx[i]); } else { for(int i = 1 ; i <= n ; i ++) { if(!vis[i]) { vis[i]=1; linkx[cnt]=i; find(cnt + 1, cost + w[cnt][i]); vis[i]=0; } } } } void solve() { KM() ; if(flag) { flag = 0 ; } else puts("") ; printf("Data Set %d, Best average difference: %.6f\n" , ++ ca , ans) ; mem(vis , 0) ; find(1 , 0) ; } int main() { int T ; scanf("%d" , &T) ; ca = 0 ; flag = 1 ; while (T --) { chu() ; init() ; solve() ; } return 0 ; }