hdoj 4494 Teamwork 【最小费用最大流】

Teamwork

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 404    Accepted Submission(s): 200


Problem Description
Some locations in city A has been destroyed in the fierce battle. So the government decides to send some workers to repair these locations. There are m kinds of workers that were trained for different skills. Each location need some number of some kinds of workers and has a schedule that at what time can the repair begins, and the time cost of repair. Any job cannot begin until all the workers required arrived. 
For example, location 1 needs 2 workers of type 1 and 3 workers of type 2, and the beginning time and time cost is 100 minute and 90 minute correspondingly, then 5 workers that satisfy the requirement should arrive before 100 minute, start working at 100 minute and get the job done at 190 minute. Notice that two different types of workers cannot replace each other, so with 3 workers of type 1 and only 2 workers of type 2, this job cannot be done. 
Workers can go from one location to another after their jobs are done. You can take the Euclidean distance between locations as the time workers need to travel between them. Each worker should be sent from a depot initially at 0 minute. Now your task is to determine the minimum number of workers needed to be sent from depot so that all the jobs can be done.
 

Input
There are multiple test cases, the integer on the first line T (T<25) indicates the number of test cases. 
Each test case begins with two integers n (<=150), the number of location(including the depot) and m(<=5), the number of different skills. 
The next line gives two integers x 0, y 0 indicates the coordinate of depot. 
Then follows n - 1 lines begins with 4 integer numbers: x i, y i, b i(b i>0), p i(p i>0), (x i, y i) gives the coordinate of the i-th location, bi gives the beginning time and pi gives the time cost. The rest of the line gives m non-negative integers v 1, v 2, ..., v m, of which the i-th number indicates the the number of workers of type i needed (for all v i, 0<=v i<10, each location at least requires one worker). 
All integers are less than 1000000 (10 6).
 

Output
For each test cases output one line, the minimum workers to be sent. It is guaranteed that there's always a feasible solution that all the jobs can be done.
 

Sample Input
  
  
2 4 1 0 0 0 1 1 1 3 1 1 3 3 4 1 0 10 1 5 4 1 0 0 0 1 1 1 3 1 1 3 3 4 1 0 3 1 5
 

Sample Output
  
  
5 9
 



题意:有n个工地和m种施工技术,给定每个工地的位置(第一个工地为工人休息地)和该工地施工的开始时间、持续时间以及完成该工地任务所需每种技术人员的人数。已知在某工地施工结束后,该工地的技术人员可以到达其它的工地上继续工作,中间的时间花费为两个工地间的欧几里得距离。现在问你最少需要多少工作人员才能完成所有工地上的任务。 注意,不同的技术人员不能互相替代。


思路:不同技术人员不可以相互替代,即每个技术人员的人数是独立、与其它技术人员人数无关的量。我们可以考虑分开来求解。因为我们要在完成施工任务的前提下,使用最少的人数。这样就是一个最小费用最大流的问题。


考虑建图:设置超级源点S,超级汇点T,将每个工地拆为三个点i, i+n, i+2*n。

一、S->i,容量为所需技术人员的人数,费用为1,表示在这条边上每新增一个技术人员就花费1。

二、i -> i+n,容量为所需技术人员的人数,费用为0。

三、i+n -> T,容量为所需技术人员的人数,费用为0。

为了保证技术人员可以从某工地i转移到其他工地j(满足两工地距离<=j工地开始施工时间 - i工地施工结束时间)

i工地可以将流量无花费的分配给j工地,考虑从S重新引一条边到i + 2*n,容量为所需技术人员的人数,费用为0,然后直接通过i + 2*n 转移流量就可以了。从i + 2*n - > j + n引边,容量为i工地可以转移的人数,费用为0。


最后求m次最小费用最大流即可。


AC代码:



#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#define INF 0x3f3f3f
#define eps 1e-4
#define MAXN (500+10)
#define MAXM (100000)
#define Ri(a) scanf("%d", &a)
#define Rl(a) scanf("%lld", &a)
#define Rf(a) scanf("%lf", &a)
#define Rs(a) scanf("%s", a)
#define Pi(a) printf("%d\n", (a))
#define Pf(a) printf("%.2lf\n", (a))
#define Pl(a) printf("%lld\n", (a))
#define Ps(a) printf("%s\n", (a))
#define W(a) while(a--)
#define CLR(a, b) memset(a, (b), sizeof(a))
#define MOD 1000000007
#define LL long long
#define lson o<<1, l, mid
#define rson o<<1|1, mid+1, r
#define ll o<<1
#define rr o<<1|1
using namespace std;
int n, m;
int num[MAXN][5];
struct MinCostMaxFlow
{
    struct Edge{
        int from, to, cap, flow, cost, next;
    };
    Edge edge[MAXM];
    int head[MAXN], edgenum;
    int pre[MAXN], dist[MAXN];
    bool vis[MAXN];
    void init(){
        edgenum = 0;
        CLR(head, -1);
    }
    void addEdge(int u, int v, int w, int c)
    {
        Edge E = {u, v, w, 0, c, head[u]};
        edge[edgenum] = E;
        head[u] = edgenum++;
        Edge E1 = {v, u, 0, 0, -c, head[v]};
        edge[edgenum] = E1;
        head[v] = edgenum++;
    }
    bool BFS(int s, int t)
    {
        queue<int> Q;
        CLR(dist, INF); CLR(pre, -1);
        CLR(vis, false);
        dist[s] = 0; vis[s] = true; Q.push(s);
        while(!Q.empty())
        {
            int u = Q.front();
            Q.pop();
            vis[u] = false;
            for(int i = head[u]; i != -1; i = edge[i].next)
            {
                Edge E = edge[i];
                if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)
                {
                    dist[E.to] = dist[u] + E.cost;
                    pre[E.to] = i;
                    if(!vis[E.to])
                    {
                        vis[E.to] = true;
                        Q.push(E.to);
                    }
                }
            }
        }
        return pre[t] != -1;
    }
    void MCMF(int s, int t, int &cost, int &flow)
    {
        cost = flow = 0;
        while(BFS(s, t))
        {
            int Min = INF;
            for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
            {
                Edge E = edge[i];
                Min = min(Min, E.cap - E.flow);
            }
            for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
            {
                edge[i].flow += Min;
                edge[i^1].flow -= Min;
                cost += Min * edge[i].cost;
            }
            flow += Min;
        }
    }
};
MinCostMaxFlow solve;
struct Point{
    double x, y, Bt, Ct;
};
Point P[MAXN];
double dis(Point A, Point B){
    return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y) * (A.y-B.y));
}
int main()
{
    int t; Ri(t);
    W(t)
    {
        Ri(n); Ri(m);
        Rf(P[0].x); Rf(P[0].y);
        n--;
        for(int i = 1; i <= n; i++)
        {
            Rf(P[i].x); Rf(P[i].y);
            Rf(P[i].Bt); Rf(P[i].Ct);
            for(int j = 1; j <= m; j++)
                Ri(num[i][j]);
        }
        int ans = 0;
        int S, T;
        S = 0; T = 3*n+1;
        for(int j = 1; j <= m; j++)
        {
            solve.init();
            for(int i = 1; i <= n; i++)
            {
                if(num[i][j] == 0)
                    continue;
                solve.addEdge(S, i, num[i][j], 1);
                solve.addEdge(S, 2 * n + i, num[i][j], 0);
                solve.addEdge(i, i + n, num[i][j], 0);
                solve.addEdge(i + n, T, num[i][j], 0);
                for(int k = 1; k <= n; k++)
                {
                    if(k == i || num[k][j] == 0) continue;
                    if(P[i].Bt - P[k].Bt - P[k].Ct >= dis(P[i], P[k]))
                        solve.addEdge(2 * n + k, n + i, num[k][j], 0);
                }
            }
            int cost, flow;
            solve.MCMF(S, T, cost, flow);
            ans += cost;
        }
        Pi(ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值