hdu 4109 Instrction Arrangement
选拔赛的题目,关键路径
继续延续着比赛的时候做不出,一回宿舍就1A的悲惨命运。
这题题意还是很易懂的,不说题意了,然后一看就是一个关键路径。一个例子就是造汽车,造各种零件的时间不同,要凑齐一些零件后才能造另一些零件,问最后汽车最早什么时候造完。
有个关键就是,如果一些零件还没有造完,而你手头上有事情可做,那么就去做,千万不要等那个零件,这样就是最优的。如果手头上没有事情可做,都是在等待的,那么只好等待了,而等待到某一个事情能开始做了,那么就做去做,就变回了上面的那种情况,但是要记得把刚才等待的时间算上去
这个其实算是模板题,关键路径大家都学过,但是不一样都做过题,我细想才发现原来我没做过这类题只是知道原理,所以这个可能就是在比赛的时候没能做出来的原因,还是自己的错,是的,真的是自己的错。比赛时我就是按照原理去写的,但是总不对,总超时,超时我就知道是哪些写进了死循环,不会是算法的错,但是就改不出,其实当时也已经放弃了。
回来后理清思路照着模拟一次,就1A了,但是时间不太好,140ms。后面会给出两个代码一个是自己的思路模拟出来,一个是模板
模拟出来的代码
/* 1.初始化,把入度为0的点入队 2.每个元素出队,就认为这个操作被执行,所以出队的时间就是操作时间 3.同样地一个元素出队就进行删弧,每删一条,就判断弧头元素入度是否为0 是的话,把它放入一个临时数组wait,不是的话不用管 4.另外在删弧过程中,要更新弧头的可操作时间,比如当前出队元素为i,当前时间为t,弧的权是w,那么弧头j的可操作时间可能为i+w 为什么是可能,因为一个元素可能不止受一条弧的影响,所以要找出最大的i+w,这个才是该元素的可操作时间 5.另外注意一点,一些元素当它们同时在队列中的时候,它们的优先级相同,也就是他们的操作时间相同,所以队列中所有元素都出队的时候 时间才能增加 6.再说回头,在一趟出队后,会有一系列的元素进入临时数组wait,并且他们的可操作时间一定是被计算完毕的了(因为指向他们的弧都已经删除了) 对这个数组进行降序排序,那么最后的元素就是可操作时间最早的,也就是可能最先被操作的 我们从最后开始扫描数组,找出可以在下1秒操作的元素,直到找不到为止。而找到的元素全部入队,因为他们下一秒将被执行 注意这里,wait数组里面如果还有元素,说明这些元素已经不受任何别的元素约束,而只是因为时间没到所以他们不能执行 而能在下一秒执行的元素已经入队了,他们将在下1秒出队并且删弧,并且看看能不能找到一些新的元素加入wait数组 而不是等待wait数组里面的元素被执行(为什么不利用这些等待时间去做别的事情呢?) 另外一点,会不会在扫描wait数组的时候,一个可操作元素都没有呢?完全有可能,也就是说,这个时间我们什么都干不了,只能等待 所以我们将时间增加,增加到wait数组最后一个元素的那个时间,那么这个元素可以从wait数组里面出来并进入队列,同样的,数组跟 它相同的元素也可以出来,那么又变回了上面的那种情况 8.整个算法的结束就是wait数组为空,也就是说,队列里面的元素都可以来,都企图删弧,企图把一些元素拉入wait数组,但是不成功, 没有元素能进去,而同时队列也为空了,那么就是全部元素都处理完了 */ #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> #include <stack> using namespace std; #define N 1010 #define M 10010 int n,m; int first[N]; int in[N],out[N]; int MAX[N]; struct edge { int u,v,w,next; }e[M]; int nume; void add(int u ,int v ,int w) { e[nume].u=u; e[nume].v=v; e[nume].w=w; e[nume].next=first[u]; first[u]=nume++; } void build() { memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(first,-1,sizeof(first)); nume=0; for(int i=0; i<m; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); out[u]++; in[v]++; add(u,v,w); } } int cmp(int a ,int b) { return MAX[a] > MAX[b]; } void solve() { int time; int wait[N],numw; queue<int>q; time=0; numw=0; while(!q.empty()) q.pop(); memset(MAX,-1,sizeof(MAX)); for(int i=0; i<n; i++) if(!in[i]) q.push(i); while(1) { time++; //这一批元素的操作时间 while(!q.empty()) //同时在队列里面的元素操作时间都是相同的 { int u; u=q.front(); q.pop(); for(int k=first[u]; k!=-1; k=e[k].next) //删弧 { int v,w; v=e[k].v; w=e[k].w; in[v]--; if(time+w > MAX[v]) MAX[v]=time+w; if(!in[v]) //可以进入wait数组 wait[numw++]=v; } } if(!numw) break; //此时队列为空,wait数组也为空 sort(wait,wait+numw,cmp); //给wait数组降序排序 //for(int i=0; i<numw; i++) printf("%d=%d\n",wait[i],MAX[wait[i]]); int flag=0; while(numw>0 && MAX[wait[numw-1]] <= time+1) //从后面扫描,看有没有元素可以在下1秒执行 { flag=1; q.push(wait[numw-1]); numw--; } if(!flag) //在wait数组里面一个都没有找到,那么将时间调到MAX[wait[numw-1]]的那个时间 { time=MAX[wait[numw-1]]; q.push(wait[numw-1]); numw--; while(numw>0 && MAX[wait[numw-1]] == time) { q.push(wait[numw-1]); numw--; } time--; //记得减1 } } printf("%d\n",time); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { build(); solve(); } return 0; }
这个算是模板啦,直接从关键路径的定义出发,并且给出了最简洁的算法,其实和上面的道理是一样的,只是不知道为什么这个代码的时间更慢
分析请移步 关键路径
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; #define N 1010 #define M 10010 int n,m; int first[N]; int in[N]; int MAX[N]; struct edge { int u,v,w,next; }e[M]; int nume; void add(int u ,int v ,int w) { e[nume].u=u; e[nume].v=v; e[nume].w=w; e[nume].next=first[u]; first[u]=nume++; } void build() { memset(in,0,sizeof(in)); memset(first,-1,sizeof(first)); nume=0; for(int i=0; i<m; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); in[v]++; add(u,v,w); } } void solve() { int T[N]; //每个点的最早完成时间 queue<int>q; memset(T,-1,sizeof(T)); while(!q.empty()) q.pop(); for(int i=0; i<n; i++) if(!in[i]) { q.push(i); T[i]=1; } while(!q.empty()) { int u,v,w; u=q.front(); q.pop(); for(int k=first[u]; k!=-1; k=e[k].next) { v=e[k].v; w=e[k].w; T[v] = max(T[v] , T[u]+w); in[v]--; if(!in[v]) q.push(v); } } int ans=-1; for(int i=0; i<n; i++) ans = max(ans , T[i]); printf("%d\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { build(); solve(); } return 0; }