【拓展km】hdu4744

23 篇文章 0 订阅

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


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值