之前说过hdu4744的正解不是费用流,其实是说不是那种纯暴力的没有针对图的性质进行的费用流...
我们知道km做最优匹配的时候是每次在相等子图上做最大匹配,如果找不到相等子图则修改顶标以扩大相等子图,那么这道题可以看出其实是一个增广多次的km,也就是说每次找到一个相等子图就做一次流量增广,同时由于一条边可以选多次,那么相等子图也会有很多个,于是修改算法就出来了,每次找一个相等子图,找到一个便进行增广,当找不到任何可增广的相等子图时则修改顶标。
跑了93ms应该是目前最快的,用G++交会慢一些,所以实际上还是比刘大师慢一些的...
#include <cstdio>
#include <cstdlib>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#define sqr(x) ((x)*(x))
const int oo=1073741819;
using namespace std;
int n;
int vx[200],vy[200],lx[200],ly[200],slack[200],f[200],g[200];
int x[200],y[200],z[200],a[200],b[200],c[200][200],w[200][200];
double dist(int i,int j)
{
return sqrt((double)(sqr(x[i]-x[j])+sqr(y[i]-y[j])+sqr(z[i]-z[j])));
}
bool km(int x)
{
if (vx[x]) return 0;
vx[x]=1;
for (int i=1;i<=n;i++) {
if (vy[i]) continue;
int tmp=lx[x]+ly[i]-w[x][i];
if (!tmp) {
vy[i]=1;
if (b[i]) {
f[x]=i,g[x]=0;
return 1;
}
for (int j=1;j<=n;j++)
if (c[j][i] && km(j)) {
f[x]=i,g[x]=j;
return 1;
}
}
else slack[i]=min(slack[i],tmp);
}
return 0;
}
int push(int x)
{
int d=a[x];
for (int i=x;i;i=g[i]) {
if (g[i]) d=min(d,c[g[i]][f[i]]);
else d=min(d,b[f[i]]);
}
int sum=0;
a[x]-=d;
for (int i=x;i;i=g[i]) {
if (g[i]) sum-=d*w[g[i]][f[i]],c[g[i]][f[i]]-=d;
else b[f[i]]-=d;
sum+=d*w[i][f[i]],c[i][f[i]]+=d;
}
return sum;
}
int main()
{
freopen("hdu4744.in","r",stdin);
freopen("hdu4744.out","w",stdout);
for (;;) {
scanf("%d",&n);
if (!n) break;
for (int i=1;i<=n;i++) {
scanf("%d%d%d%d",&x[i],&y[i],&z[i],&a[i]);
b[i]=a[i];
lx[i]=0,ly[i]=0;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) {
if (i!=j) w[i][j]=-floor(dist(i,j));
else w[i][j]=-oo;
c[i][j]=0;
lx[i]=max(lx[i],w[i][j]);
}
int ans=0;
for (int i=1;i<=n;i++) {
for (;a[i];) {
for (int j=1;j<=n;j++) slack[j]=oo;
for (;a[i];) {
for (int j=1;j<=n;j++) vx[j]=vy[j]=0;
if (km(i))
ans+=push(i);
else break;
}
if (!a[i]) break;
int d=oo;
for (int i=1;i<=n;i++)
if (!vy[i]) d=min(d,slack[i]);
for (int i=1;i<=n;i++) {
if (vx[i]) lx[i]-=d;
if (vy[i]) ly[i]+=d;
}
}
}
for (int i=1;i<=n;i++)
if (c[i][i]) ans=1;
printf("%d\n",-ans);
}
return 0;
}