http://acm.hdu.edu.cn/showproblem.php?pid=3572
题意:给你n个任务,m个机器,每个任务有花费时间w、开始时间s、结束时间t,每个任务可以以每天为单位断断续续执行,但在同一天一台机器只能执行一个任务。问是否存在一个可行的时间安排能够使他们全部完成。
ps:因为引用,贡献了20多个TLE。。都怪我蠢。。
思路:这题难在建图。要想达到目的有两个指标,一个是做完全部任务,另一个就是不超过时间限制,于是就把这两项当成分界线。建立一个超级源点连向所有任务,容量为该任务所需要的天数。每个任务连向该任务可以合法完成的所有工作日,容量为1,代表每天工作量为1。每个工作日连向超级汇点,容量为机器数量,表示每天的最大加工量。如果从源点到汇点的最大流等于所有工作所需的时间和,就说明中间没有任何一个任务时间冲突造成的无法完成,则总体安排可以完成。整体思路还需好好总结。
[前方高能]
直到这道题才发现我的模板有问题。。就是这一段:(dfs中的)
for(int& i = cur[x]; i != -1; i = edge[i].next)
一直用的邻接表前面都没加引用,这里不加引用竟然超时??在这里愣是懵逼了好久。。好在有人提醒,下面有递归操作!!
f = dfs(E.to, min(res, E.cap-E.flow), endd);
但是递归操作又怎样呢?注意我们这里处理的是前向星,在这里建议看下这位大牛对前向星的理解。从这篇博客我们知道,head[i]保存的是以i为起点的所有边中编号最大的那个。就拿那个大牛博客里的例子来了:(如果想懂一定要看完他的这篇文章!)
edge[0].to = 2; edge[0].next = -1; head[1] = 0;
edge[1].to = 3; edge[1].next = -1; head[2] = 1;
edge[2].to = 4; edge[2],next = -1; head[3] = 2;
edge[3].to = 3; edge[3].next = 0; head[1] = 3;
edge[4].to = 1; edge[4].next = -1; head[4] = 4;
edge[5].to = 5; edge[5].next = 3; head[1] = 5;
edge[6].to = 5; edge[6].next = 4; head[4] = 6;
遍历以节点1为起点的三条边,编号分别为0,3,5。
如果没有递归,比如我们遍历起点为1的边,head[1]=5,也就是先遍历编号为5的边,然后是i = edge[5].next等于3,找到了编号为3的边,然后继续i = edge[3].next等于0,找到编号为0的边。这里加了递归,我们遍历起点为1的边,head[1]=5,然后是i = edge[5].next等于3。(虽然直观上在分层的dfs中下一次不可能遍历相同起点的边,但是我们这里注意,尼玛可能有重边啊!!dfs又不是一次深搜就找到路线,即使对于分层的也需要回溯查找,这样就完全有可能经过同一个起点!!)那么见证奇迹的时刻来了,递归下去同样是对i赋值head[1]依然等于5啊,这个5是不是遍历过了?!!那下一次找编号为0的边,不是还要遍历3和5?这就是超时的原因所在啊!!
解决方法就是前面加个引用,做到head和i的同步更新,不过因为要多次dfs,所以复制出一个cur,对cur更新。
本题是用前向星做的,没用前向星的请直接无视= =。话说前面两题都是用错的模板过得,说明数据不怎么样嘛!不过也有看到不加引用的前向星。。orz。。不过通过这道题,使我对前向星有了一个更加深刻的理解,想必也是极好的。。不说了把前面两个模板改了555
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <stack>
#include <ctype.h>
using namespace std;
typedef long long LL;
const int N = 1000005;
const int INF = 0x3f3f3f3f;
int head[N], dis[N], cur[N];
bool vis[N];
int n, m, cnt, sink, sum;
struct Edge
{
int to, cap, flow, next;
}edge[N];
void add(int u, int v, int w)
{
edge[cnt] = (struct Edge){v, w, 0, head[u]};
head[u] = cnt++;
edge[cnt] = (struct Edge){u, 0, 0, head[v]};
head[v] = cnt++;
}
bool bfs(int start, int endd)
{
memset(dis, -1, sizeof(dis));
memset(vis, false, sizeof(vis));
queue<int>que;
dis[start] = 0;
vis[start] = true;
que.push(start);
while(!que.empty())
{
int u = que.front();
que.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(!vis[E.to] && E.flow<E.cap)
{
dis[E.to] = dis[u]+1;
vis[E.to] = true;
if(E.to == endd) return true;
que.push(E.to);
}
}
}
return false;
}
int dfs(int x, int res, int endd)
{
if(x == endd || res == 0) return res;
int flow = 0, f;
for(int& i = cur[x]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dis[E.to] == dis[x]+1)
{
f = dfs(E.to, min(res, E.cap-E.flow), endd);
if(f>0)
{
edge[i].flow += f;
edge[i^1].flow -= f;
flow += f;
res -= f;
if(res == 0) break;
}
}
}
return flow;
}
int max_flow(int start, int endd)
{
int flow = 0;
while(bfs(start, endd))
{
memcpy(cur, head, sizeof(head));
flow += dfs(start, INF, endd);
}
return flow;
}
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void getmap()
{
int s, t, w, last;
last = 0;
sum = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%d%d%d", &w, &s, &t);
add(0, i, w);
last = max(last, t);
for(int j = s; j <= t; j++)
add(i, n+j, 1);
sum += w;
}
sink = n+last+1;
for(int i = n+1; i <= n+last; i++)
add(i, sink, m);
}
int main()
{
// freopen("in.txt", "r", stdin);
int t0, Case = 1;
scanf("%d", &t0);
while(t0--)
{
init();
getmap();
int ans = max_flow(0, sink);
if(ans == sum) printf("Case %d: Yes\n\n", Case++);
else printf("Case %d: No\n\n", Case++);
}
return 0;
}