题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1076
我的链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=19651#problem/H
题意:
你受聘于当地的垃圾处理公司任CEO,你的一项工作是处理引进的垃圾,以及分类垃圾以进行循环利用。每天,垃圾会有几个集装箱运来,每一个集装箱都包含几种垃圾。给定集装箱里垃圾的数目n,请找出最佳的途径去分类这些垃圾。分类垃圾即把每种垃圾分开装到不同的集装箱中。每个集装箱的容量都是无限的。搬动一个单位的垃圾需要耗费代价,从集装箱i到j是1(i≠j,否则代价为0),你必须把代价减到最小。
思路:
求最大权匹配的KM算法,只需把所有边的权值取相反数,求最大权匹配,结果再取相反数即可。
把垃圾筒作为点集u,垃圾总类作为点集v,建图w[i][j]表示把j类垃圾放入i 桶的代价。
分析:用前面的KM模板时间复杂度为O(N^4)容易超时。
前面的版本修改顶标:
枚举s和t中的每一个元素,根据定义计算最小值,每次修时间为O(N^2),总时间为O(N^4).
快速修改顶标法:
给t中的每个节点y定义松弛量。slack(y) = min{ l[x]+l[y]-w[x][y] }
则a变为a = min{ slack(y)| y in t`}
计算所有slack要O(N)时间。每次增广后最多修改N次顶标,所以每次增广后修改顶标总时间为O(N^2),程序总时间降为O(N^3).
//Accepted 216 KB 31 ms C++ 1503 B 2013-02-26 20:03:10 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=155; const int inf=1000; int w[maxn][maxn]; int lx[maxn],ly[maxn]; //顶标 bool s[maxn],t[maxn]; int match[maxn]; int slack[maxn]; //点集v的松弛量 int n; bool hungary(int u) { s[u] = true; for(int v=1;v<=n;v++) { if(!t[v] && lx[u]+ly[v] == w[u][v]) { t[v] = true; if(match[v]==-1 || hungary(match[v])) { match[v]=u; return true; } } else if(slack[v] > lx[u]+ly[v]-w[u][v])//修改松弛量 slack[v] = lx[u]+ly[v]-w[u][v]; } return false; } int KM() { int sum=0; memset(match,-1,sizeof(match)); memset(ly,0,sizeof(ly)); for(int i=1;i<=n;i++) { lx[i]= -inf; for(int j=1;j<=n;j++) lx[i]=max(lx[i],w[i][j]); } for(int i=1;i<=n;i++) { while(true) { memset(s,false,sizeof(s)); memset(t,false,sizeof(t)); for(int j=1;j<=n;j++) slack[j]=inf; if(hungary(i)) break; else { int a=inf; for(int j=1;j<=n;j++) if(!t[j] && slack[j]<a) a=slack[j]; for(int j=1;j<=n;j++) { if(s[j]) lx[j]-=a; if(t[j]) ly[j]+=a; } } } } for(int i=1;i<=n;i++) sum+=w[match[i]][i]; return sum; } int main() { int weight; while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) { weight=0; for(int j=1;j<=n;j++) { scanf("%d",&w[i][j]); weight+=w[i][j]; } for(int j=1;j<=n;j++) w[i][j]=weight-w[i][j]; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) w[i][j] = -w[i][j]; } printf("%d\n",-KM()); } return 0; }