HDU 3572 Task Schedule(最大流问题,sap算法)

来源:互联网 发布:前端工程师 程序员 编辑:程序博客网 时间:2024/05/16 15:26
/*题意:用m个机器,处理n个任务,每个任务必须在[si,ei]时间段完成,需要pi天才能完成。每个机器只能处理一个任务,即每天只能处理m个任务。题解:可以采用贪心法处理,区间覆盖问题,可以参见刘汝佳的书。或者采用最大流,建图:把每个任务和每一天看做一个点,增加源点s和汇点t,在s和每个任务之间连一条边,容量为持续天数;在每一天和t之间连一条边,容量为m;在每个任务和对应天数之间连一条边,容量为1。然后最大流量即为所求。实现:sap算法,算法复杂度比Dinic更低。sap详细介绍参见:http://chuanwang66.iteye.com/blog/1450715其中涉及到三种优化,这一部分讲的特别好。主要思路为:首先建立邻接表,然后建立层次图,查找最短最广路径。*///sap算法递归实现#include <iostream>using namespace std;const int nMax = 2000;const int mMax = 500000;const int INF = 0x7fffffff;struct Edge{int v, w, next;Edge(){}Edge(int v, int w, int next):v(v), w(w), next(next){}}adj[mMax];int ind;int head[nMax];//顶点对应的第一条弧int dis[nMax];//顶点所在层次数int vn[nMax];//各个层次顶点个数int t, n, m;int pi, si, ei;int source, sink, NV;//源点、汇点、总顶点数void addEdge(int u, int v, int w){adj[ind] = Edge(v, w, head[u]);head[u] = ind ++;adj[ind] = Edge(u, 0, head[v]);head[v] = ind ++;}int dfs(int cur, int cost){if(cur == sink)return cost;int leave = cost;//leave表示剩余可利用流量int MinDis = NV;for(int id = head[cur]; id != -1; id = adj[id].next){int v = adj[id].v;if(adj[id].w){if(dis[v] + 1 == dis[cur]){int MinFlow = min(leave, adj[id].w);//原来这里出错,需要比较的是leave和adj[id].w的值,因为leave的值在不断变化MinFlow = dfs(v, MinFlow);//找到从v出发到sink的最广路径的最小流量adj[id].w -= MinFlow;adj[id ^ 1].w += MinFlow;leave -= MinFlow;if(dis[source] >= NV) return cost - leave;//总流量 - 剩余流量if(leave == 0) break;}if(dis[v] < MinDis)//MinDis存储与cur相连顶点中最小层次数,便于没有找到可行弧时候的更新。MinDis = dis[v];}}if(leave == cost)//总流量等于剩余流量,则没有查找到可行弧{if((-- vn[dis[cur]]) == 0) dis[source] = NV;dis[cur] = MinDis + 1;vn[dis[cur]] ++;}return cost - leave;}int sap(int s, int e){source = s;sink = e;NV = e + 1;memset(dis, 0, sizeof(dis));memset(vn, 0, sizeof(vn));vn[0] = NV;int ans = 0;while(dis[source] < NV)//使用dis[source] >= NV作为终止条件{ans += dfs(s,INF);}return ans;}int main(){//freopen("e://data.in", "r", stdin);int s, e;scanf("%d", &t);for(int cas = 1; cas <= t; cas ++){int Max = 0;int sum = 0;s = 0;ind = 0;memset(head, -1, sizeof(head));scanf("%d %d", &n, &m);for(int i = 1; i <= n; ++ i){scanf("%d %d %d", &pi, &si, &ei);sum += pi;addEdge(s, i, pi);Max = max(Max, ei);for(int j = si; j <= ei; ++ j)addEdge(i, n + j, 1);}e = n + Max + 1;for(int i = 1; i <= Max; ++ i)addEdge(n + i, e, m);int a;if((a = sap(s, e)) == sum)printf("Case %d: Yes\n\n", cas);elseprintf("Case %d: No\n\n", cas);}return 0;}//非递归实现#include <iostream>//#define TESTusing namespace std;const int nMax = 2000;const int mMax = 500000;const int INF = 0x7fffffff;struct Edge{int u, v, w;int next;Edge(){}Edge(int u, int v, int w, int next):u(u), v(v), w(w), next(next){}}adj[mMax];int ind;int dis[nMax];//距离int pre[nMax];//路径int head[nMax];//第一条弧int flow[nMax];//允许弧中最小流int vn[nMax];//每层对应顶点个数int cur[nMax];//当前弧int t, n, m;int pi, si, ei;void addEdge(int u, int v, int w)//相邻接表中增加边{adj[ind] = Edge(u, v, w, head[u]);head[u] = ind ++;adj[ind] = Edge(v, u, 0, head[v]);head[v] = ind ++;}int sap(int s, int e, int n)//sap算法,最短增广路经{int u = s;int MaxFlow = 0;memset(dis, 0, sizeof(dis));flow[s] = INF;memset(vn, 0, sizeof(vn));pre[s] = -1;for(int i = 0; i < n; ++ i)cur[i] = head[i];//每一个顶点对应的当前弧while(d[s] < n)//其实这里不需要什么d[s] < n,因为终止条件只需要层次图出现中断即可,所以while(1)同样正确{bool flag = false;if(u == e){MaxFlow += flow[e];for(int v = pre[e]; v != -1; v = pre[v]){int id = cur[v];adj[id].w -= flow[e];//对增广路经进行处理adj[id ^ 1].w += flow[e];if(adj[id].w == 0) u = v;//退回到权值为0的弧首,因为如果等于0,后面的节点肯定无法到达的}}for(int id  = cur[u]; id != -1; id = adj[id].next)//寻找可行弧{int v = adj[id].v;if(adj[id].w > 0 && dis[v] + 1 == dis[u]){flag = true;pre[v] = u;flow[v] = min(flow[u], adj[id].w);cur[u] = id;u = v;break;}}if(!flag)//找不到可行弧,所以需要变更dis[u]的值{if((-- vn[dis[u]]) == 0) break;//层次图中断int MinDis = n;//寻找相连边中,最小dis[v]值cur[u] = head[u];for(int id = head[u]; id != -1; id = adj[id].next){int v = adj[id].v;if(adj[id].w > 0 && dis[v] < MinDis)//edge[i].w > 0这个条件需要注意,代表还可达{MinDis = dis[v];cur[u] = id;}}dis[u] = MinDis + 1;//对dis[u]进行重新定义vn[dis[u]] ++;if(u != s) u = pre[u];//回溯}}return MaxFlow;}int main(){//freopen("e://data.in", "r", stdin);//freopen("e://data2.out", "w", stdout);int s, e;scanf("%d", &t);for(int cas = 1; cas <= t; cas ++){int Max = 0;int sum = 0;s = 0;ind = 0;memset(head, -1, sizeof(head));scanf("%d %d", &n, &m);for(int i = 1; i <= n; ++ i){scanf("%d %d %d", &pi, &si, &ei);sum += pi;addEdge(0, i, pi);Max = max(Max, ei);for(int j = si; j <= ei; ++ j)addEdge(i, n + j, 1);}e = n + Max + 1;for(int i = 1; i <= Max; ++ i)addEdge(n + i, e, m);if(sap(s, e, e + 1) == sum)printf("Case %d: Yes\n\n", cas);elseprintf("Case %d: No\n\n", cas);}return 0;}


原创粉丝点击