/* 题意:用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); else printf("Case %d: No\n\n", cas); } return 0; } //非递归实现 #include <iostream> //#define TEST using 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); else printf("Case %d: No\n\n", cas); } return 0; }