之前说过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; }