hdu 3572 Task Schedule(当前弧优化Dinic算法)

hdu 3572 Task Schedule(当前弧优化Dinic算法)

Problem Description

Our geometry princess XMM has stoped her study in computational geometry to concentrate on her newly opened factory. Her factory has introduced M new machines in order to process the coming N tasks. For the i-th task, the factory has to start processing it at or after day Si, process it for Pi days, and finish the task before or at day Ei. A machine can only work on one task at a time, and each task can be processed by at most one machine at a time. However, a task can be interrupted and processed on different machines on different days. 
Now she wonders whether he has a feasible schedule to finish all the tasks in time. She turns to you for help.

Input

On the first line comes an integer T(T<=20), indicating the number of test cases.
You are given two integer N(N<=500) and M(M<=200) on the first line of each test case. Then on each of next N lines are three integers Pi, Si and Ei (1<=Pi, Si, Ei<=500), which have the meaning described in the description. It is guaranteed that in a feasible schedule every task that can be finished will be done before or at its end day.

Output

For each test case, print “Case x: ” first, where x is the case number. If there exists a feasible schedule to finish all the tasks, print “Yes”, otherwise print “No”.
Print a blank line after each test case.

Sample Input

2
4 3
1 3 5
1 1 4
2 3 7
3 5 9
 
2 2
2 1 3
1 2 2

Sample Output

Case 1: Yes
 
Case 2: Yes
解题思路:此题关键在于建图,跑最大流来判断是否已达到满流。题意:有n个任务,m台机器。每个任务有最早才能开始做的时间s_i,截止时间e_i,和完成该任务所需要的时间p_i。每个任务可以分段进行,但在同一天一台机器最多只能执行一个任务,问是否有可行的工作时间来完成所有任务。做法:建立一个超级源点s=0和一个超级汇点t=1001。源点s和每个任务i建边,边权为p_i,表示完成该任务需要的天数。对于每一个任务i,将编号i与编号n+[s_i~e_i]中每个编号建边,边权为1,表示将任务i分在第n+s_i天~第n+e_i天来完成,再将编号n+[s_i~e_i]与汇点t建边,边权为m,表示每一天(编号为n+j)最多同时运行m台机器来完成8天的任务单位量。但由于建的边数太多,用裸的Dinic跑最大流会TLE,怎么优化呢?采用当前弧优化:因为每次dfs找增广路的过程中都是从每个顶点指向的第1(编号为0)条边开始遍历的,而如果第1条边已达到满流,则会继续遍历第2条边....直至找到汇点,事实上这个递归的过程就造成了很多不必要浪费的时间,所以在dfs的过程中应标记一下每个顶点v当前能到达的第curfir[v]条边,说明顶点v的第0条边~第curfir[v]-1条边都已达满流或者流不到汇点,这样时间复杂度就大大降低了。注意:每次bfs给图重新分层次时,需要清空当前弧数组cirfir[],表示初始时从每个顶点v的第1(编号为0)条边开始遍历。
AC代码(124ms):
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int INF=0x3f3f3f3f;
 4 const int maxn=1005;
 5 struct edge{ int to,cap;size_t rev;
 6     edge(int _to, int _cap, size_t _rev):to(_to),cap(_cap),rev(_rev){}
 7 };
 8 int T,n,m,p,s,e,tot,level[maxn];queue<int> que;vector<edge> G[maxn];size_t curfir[maxn];//当前弧数组
 9 void add_edge(int from,int to,int cap){
10     G[from].push_back(edge(to,cap,G[to].size()));
11     G[to].push_back(edge(from,0,G[from].size()-1));
12 }
13 bool bfs(int s,int t){
14     memset(level,-1,sizeof(level));
15     while(!que.empty())que.pop();
16     level[s]=0;
17     que.push(s);
18     while(!que.empty()){
19         int v=que.front();que.pop();
20         for(size_t i=0;i<G[v].size();++i){
21             edge &e=G[v][i];
22             if(e.cap>0&&level[e.to]<0){
23                 level[e.to]=level[v]+1;
24                 que.push(e.to);
25             }
26         }
27     }
28     return level[t]<0?false:true;
29 }
30 int dfs(int v,int t,int f){
31     if(v==t)return f;
32     for(size_t &i=curfir[v];i<G[v].size();++i){//从v的第curfir[v]条边开始,采用引用的方法,同时改变本身的值
33         //因为节点v的第0~curfir[v]-1条边已达到满流了,所以无需重新遍历--->核心优化
34         edge &e=G[v][i];
35         if(e.cap>0&&(level[v]+1==level[e.to])){
36             int d=dfs(e.to,t,min(f,e.cap));
37             if(d>0){
38                 e.cap-=d;
39                 G[e.to][e.rev].cap+=d;
40                 return d;
41             }
42         }
43     }
44     return 0;
45 }
46 int max_flow(int s,int t){
47     int f,flow=0;
48     while(bfs(s,t)){
49         memset(curfir,0,sizeof(curfir));//重新将图分层之后就清空数组,从第0条边开始遍历
50         while((f=dfs(s,t,INF))>0)flow+=f;
51     }
52     return flow;
53 }
54 int main(){
55     while(~scanf("%d",&T)){
56         for(int cas=1;cas<=T;++cas){
57             scanf("%d%d",&n,&m);tot=0;
58             for(int i=0;i<maxn;++i)G[i].clear();
59             for(int i=1;i<=500;++i)add_edge(500+i,1001,m);
60             for(int i=1;i<=n;++i){
61                 scanf("%d%d%d",&p,&s,&e);
62                 add_edge(0,i,p);tot+=p;//tot为总时间
63                 for(int j=s;j<=e;++j)add_edge(i,500+j,1);
64             }
65             printf("Case %d: %s

",cas,max_flow(0,1001)==tot?"Yes":"No");
66         }
67     }
68     return 0;
69 }