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;
}

 

 

转载于:https://www.cnblogs.com/scau20110726/archive/2013/03/23/2977701.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值