题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1076
我的链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=19651#problem/H
1076. Trash
Memory Limit: 16 MB
题意:
你受聘于当地的垃圾处理公司任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;
}