HDU 4744 Starloop System(ZKW费用流)

解题思路:

  新建一个源点 S 和汇点 T。将每个点拆成两个点Ai 和 Bi。S到Ai连一个容量为Wi ,费用为 0 的边 , Bi 到 T连一个容量 为Wi, 费用 为0的 边。每个 i 和 j之间连一个容量为INF,费用为两点之间距离 的边。求最小费用最大流。如果最大流不等于Wi的和,则输出-1,否则输出最小费用。

  普通的 最小费用流会超时,因此需要采用ZKW费用流。

#include <cstdio>
#include <queue>
#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
#define mul(a) (a)*(a)
using namespace std;
const int Maxn = 210;
const int Maxm = 100010;
const int inf = 0x3f3f3f3f;
struct ZKW_flow
{
    int st, ed, ecnt, n;
    int head[Maxn];
    int cap[Maxm], cost[Maxm], to[Maxm], next[Maxm];
    void init()
    {
        memset(head, -1, sizeof(head));
        ecnt = 2;
    }
    void AddEdge(int u, int v, int cc, int ww)
    {
        cap[ecnt] = cc; cost[ecnt] = ww; to[ecnt] = v;
        next[ecnt] = head[u]; head[u] = ecnt++;
        cap[ecnt] = 0; cost[ecnt] = -ww; to[ecnt] = u;
        next[ecnt] = head[v]; head[v] = ecnt++;
    }
    int dis[Maxn];
    void SPFA()
    {
        for(int i = 0; i <= n; ++i) dis[i] = inf;
        priority_queue<pair<int, int> > Q;
        dis[st] = 0;
        Q.push(make_pair(0, st));
        while(!Q.empty()){
            int u = Q.top().second, d = -Q.top().first;
            Q.pop();
            if(dis[u] != d) continue;
            for(int p = head[u]; p!=-1; p = next[p]){
                int &v = to[p];
                if(cap[p] && dis[v] > d + cost[p]){
                    dis[v] = d + cost[p];
                    Q.push(make_pair(-dis[v], v));
                }
            }
        }
        for(int i = 0; i <= n; ++i) dis[i] = dis[ed] - dis[i];
    }
    int minCost, maxFlow;
    bool use[Maxn];
    int add_flow(int u, int flow)
    {
        if(u == ed)
        {
            maxFlow += flow;
            minCost += dis[st] * flow;
            return flow;
        }
        use[u] = true;
        int now = flow;
        for(int p = head[u]; p!=-1; p = next[p])
        {
            int &v = to[p];
            if(cap[p] && !use[v] && dis[u] == dis[v] + cost[p])
            {
                int tmp = add_flow(v, min(now, cap[p]));
                cap[p] -= tmp;
                cap[p^1] += tmp;
                now -= tmp;
                if(!now) break;
            }
        }
        return flow - now;
    }

    bool modify_label()
    {
        int d = inf;
        for(int u = 0; u <= n; ++u) if(use[u])
            for(int p = head[u]; p!=-1; p = next[p])
            {
                int &v = to[p];
                if(cap[p] && !use[v]) d = min(d, dis[v] + cost[p] - dis[u]);
            }
        if(d == inf) return false;
        for(int i = 0; i <= n; ++i) if(use[i]) dis[i] += d;
        return true;
    }

    int ZKW(int ss, int tt, int nn)
    {
        st = ss, ed = tt, n = nn;
        minCost = maxFlow = 0;
        SPFA();
        while(true)
        {
            while(true)
            {
                for(int i = 0; i <= n; ++i) use[i] = 0;
                if(!add_flow(st, inf)) break;
            }
            if(!modify_label()) break;
        }
        return minCost;
    }
} G;
struct Point
{
    int x , y , z;
    int w;
}P[Maxn];
int dis(Point A , Point B)
{
    return (int) sqrt(1.0 * mul(A.x - B.x) + 1.0 * mul(A.y - B.y) + 1.0 * mul(A.z - B.z));
}
int main()
{
    int N;
    while(scanf("%d",&N)!=EOF && N)
    {
        G.init();
        int sum = 0;
        for(int i=1;i<=N;i++)
        {
            scanf("%d%d%d%d",&P[i].x , &P[i].y , &P[i].z , &P[i].w);
            sum += P[i].w;
        }
        int s = 0 , t = 2 * N + 1;
        for(int i=1;i<=N;i++)
        {
            G.AddEdge(s , i ,  P[i].w , 0);
            G.AddEdge(N + i , t , P[i].w , 0);
            for(int j=i+1;j<=N;j++)
            {
                int  d = dis(P[i] , P[j]);
                G.AddEdge(i , N + j , inf , d);
                G.AddEdge(j , N + i , inf , d);
            }
        }
        int ans = G.ZKW(s , t , t + 1);
        if(G.maxFlow != sum) printf("-1\n");
        else printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值