hdu 4494 Teamwork(费用流)

最开始我是想的,把每个点拆成m个点,然后用一次费用流搞,对于那些可以重复利用的工人,添加一些cost为-1的弧。很逼真把,Why wa。。。

其实每个兵种是相互独立的,那么就可以对于每个兵种,分别搞一次费用流,最后累加ans。这样代码就好撸很多了呢。。。

费用流建图:显然每个点要拆成两个点,一个入(从s向其连<INF, 1>的弧,简称x点),一个出(从该点向t连<need, 0>的弧,简称y点)。那么对于i,j两个点,如果end[i] + dist(i, j) <= start[j]的时候怎么办?如果直接从i的y点向j的j的x点连弧会使i本身的y点->t的弧无法满流,显然会影响最优解。所以每个点其实呀拆成3个点,新加的z点,代表去某点的工人重复利用的流量。首先要从s向每个z点连<need, 0>的弧,那么对于重复利用的情况,从i的z点向j的y点连<need, 0>的弧就行啦!

ps:用ZKW费用流快多了。。。78ms。。。。

#include<functional>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define REP(i, n) for(int i=0; i<n; i++)
#define FF(i, a, b) for(int i=a; i<b; i++)
#define CLR(a, b) memset(a, b, sizeof(a))
#define PB push_back
using namespace std;

const int MAXN = 455;
const int MAXE = MAXN*MAXN/2;
const int INF = 1e9;
struct ZKW_flow{
    int st, ed, ecnt, n;
    int head[MAXN];
    int cap[MAXE], cost[MAXE], to[MAXE], next[MAXE], dis[MAXN]; ;

    void init(){
        memset(head, 0, 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++;
    }

    void SPFA(){
        REP(i, n+1) 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; 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));
                }
            }
        }
        REP(i, n+1) 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; 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;
        REP(u, n+1) if(use[u])
            for(int p = head[u]; p; 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;
        REP(i, n+1) if(use[i]) dis[i] += d;
        return true;
    }

    int Mincost(int ss, int tt, int nn){
        st = ss, ed = tt, n = nn;
        minCost = maxFlow = 0;
        SPFA();
        while(true){
            while(true){
                CLR(use, 0);
                if(!add_flow(st, INF)) break;
            }
            if(!modify_label()) break;
        }
        return minCost;
    }
}solver;

int T, n, m, s, t;
struct Point
{
    double x, y, st, ed;
    int need[11];
    void get()
    {
        scanf("%lf%lf%lf%lf", &x, &y, &st, &ed);
        ed += st;
        REP(i, m) scanf("%d", &need[i]);
    }
    double dist(Point rhs)
    {
        return sqrt((x-rhs.x)*(x-rhs.x) + (y-rhs.y)*(y-rhs.y));
    }
}pt[155];

int gao()
{
    int ret = 0;
    REP(j, m)
    {
        solver.init();
        FF(i, 1, n)
        {
            solver.AddEdge(s, i, INF, 1);
            solver.AddEdge(i, i + 2*n, pt[i].need[j], 0);
            solver.AddEdge(i + 2*n, t, pt[i].need[j], 0);
            solver.AddEdge(s, i+n, pt[i].need[j], 0);
        }
        FF(i, 1, n) FF(k, 1, n) if(i != k)
        {
            if(pt[i].ed + pt[i].dist(pt[k]) > pt[k].st) continue;
            solver.AddEdge(i + n, k + 2*n, pt[i].need[j], 0);
        }
        ret += solver.Mincost(s, t, t);
    }
    return ret;
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        s = 0;  t = n*3 + 1;
        scanf("%lf%lf", &pt[0].x, &pt[0].y);
        FF(i, 1, n) pt[i].get();
        printf("%d\n", gao());
    }
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值