hdu 4494 Teamwork(多源多汇最小费用最大流,巧妙构图)

来源:互联网 发布:url传递json对象 编辑:程序博客网 时间:2024/05/20 18:51

Teamwork

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


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 x0, y0 indicates the coordinate of depot. 
Then follows n - 1 lines begins with 4 integer numbers: xi, yi, bi(bi>0), pi(pi>0), (xi, yi) 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 v1, v2, ..., vm, of which the i-th number indicates the the number of workers of type i needed (for all vi, 0<=vi<10, each location at least requires one worker). 
All integers are less than 1000000 (106).
 

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
59
题意:有n个地方和m种技能,每种工人只能有一种技能,每个地方需要多种不同技能的人,数量也各不相同。

一开始工人们都在0号地方仓库里,派工人到1~n-1号地方去工作不需要时间,但是工人在一个地点工作完后可以跑到另一个未开始的地点进行工作,等那个地点所需人都来齐后可以开始工作,从一个地点跑到另一个地方所需时间是欧几里得距离,现在给出每个地点的开始时间和工作时间,工人之间不能替换(不会2技能的工人不能顶替会2技能的工人),求仓库最少需要派出几个工人?

思路:分析问题后可以发现,工人之间没有任何关系,因为开始时间已经给定了,所以我们必须保证的是所有需要的人都必须在开始时间前到达,所以工人之间没有任何关系。

那么我们枚举不同技能的工人,求出某一个技能的工人完成自己的工作至少需要几个人,最后累加起来就是答案了。

不过无奈我想到这里就不会构图了 = =,真是蒟蒻。

具体的构图方式是:

首先设立超级源点s和超级汇点t

s连接所有的地点,容量为INF或者这个地点所需要的人数,费用是1(流量大小即是人数)

然后所有的地点都连向t  这样表示不考虑一个工人做完后移动到另一个地点的情况,求出来的必定是所有地方的工人数总和

那么我们现在再把地点给拆成两个点,这些点如果时间满足题目的话就连向1~n-1中对应的那个地点即可,求出能到某个地点的人数即可

具体实现可以看代码,应该不难看懂

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <queue>using namespace std;#define N 1050#define INF 999999999#define eps 1e-8struct Edge{    int u,v,next,cap,cost;} edge[N*N];struct Node{    double x,y,b,p;    int num[6];}e[N];int cnt,head[N];char ma[55][55];int vis[N],pp[N],d[N],sumflow;void init(){    cnt=0;    memset(head,-1,sizeof(head));}void addedge(int u,int v,int cap,int cost){    edge[cnt].u=u;    edge[cnt].v=v;    edge[cnt].cap=cap;    edge[cnt].cost=cost;    edge[cnt].next=head[u];    head[u]=cnt++;    edge[cnt].u=v;    edge[cnt].v=u;    edge[cnt].cap=0;    edge[cnt].cost=-cost;    edge[cnt].next=head[v];    head[v]=cnt++;}int spfa(int s,int t,int n){    queue<int>q;    memset(vis,0,sizeof(vis));    memset(pp,-1,sizeof(pp));///pp[i]表示最短路径上以i为终点的边的编号    for(int i=0; i<=n; i++)        d[i]=INF;    d[s]=0;    vis[s]=1;    q.push(s);    while(!q.empty())    {        int u=q.front();        q.pop();        vis[u]=0;        for(int i=head[u]; i!=-1; i=edge[i].next)        {            int v=edge[i].v;            if(edge[i].cap>0&&d[v]>d[u]+edge[i].cost)            {                d[v]=d[u]+edge[i].cost;                pp[v]=i;                if(!vis[v])                {                    vis[v]=1;                    q.push(v);                }            }        }    }    if(d[t]==INF) return 0;///找不到一条到终点的路    return 1;}int MCMF(int s,int t,int n){    int mincost=0,minflow,flow=0;///最大费用,路径中最小流量,总流量    while(spfa(s,t,n))///找当前的最长路    {        minflow=INF+1;        for(int i=pp[t]; i!=-1; i=pp[edge[i].u])            minflow=min(minflow,edge[i].cap);///从路径中找最小的流量        flow+=minflow;///总流量加上最小流量        for(int i=pp[t]; i!=-1; i=pp[edge[i].u])        {            edge[i].cap-=minflow;///当前边减去最小流量            edge[i^1].cap+=minflow;///反向边加上最小流量        }        mincost+=d[t]*minflow;///最小费用等于路径和*每条路径的流量(经过多少次)    }    sumflow=flow;    return mincost;}double dist(double x,double y,double x1,double y1){    return sqrt((x-x1)*(x-x1)+(y-y1)*(y-y1));}int main(){    int T,n,m;    double x,y;    scanf("%d",&T);    while(T--)    {        scanf("%d %d",&n,&m);        scanf("%lf %lf",&x,&y);        for(int i=1;i<n;i++)        {            scanf("%lf %lf %lf %lf",&e[i].x,&e[i].y,&e[i].b,&e[i].p);            for(int j=1;j<=m;j++)                scanf("%d",&e[i].num[j]);        }        int ans=0;        for(int i=1;i<=m;i++)        {            init();            int s=0,t=2*n;            for(int j=1;j<n;j++)                    {                        addedge(s,j,e[j].num[i],1);                        addedge(j,t,e[j].num[i],0);                        addedge(s,j+n,e[j].num[i],0);                    }            for(int j=1;j<n;j++)                for(int k=1;k<n;k++)                    if(j!=k&&dist(e[j].x,e[j].y,e[k].x,e[k].y)+e[j].b+e[j].p-e[k].b<eps)                        addedge(n+j,k,INF,0);            ans+=MCMF(s,t,t);        }        printf("%d\n",ans);    }    return 0;}







0 0
原创粉丝点击