zoukankan      html  css  js  c++  java
  • 机房测试:lunch(贪心+最短路)

    题目:

     分析:

    由数据3得:既然所有人都要学会,肯定是越早学越优。(贪心重要思路)

    所以转移就是:dis[v]=max( dis[u] ,L ),u学会之后传授给v的条件是:u先学会,传授的时间在吃饭的时间内

    在最短路上转移即可

    再考虑有人必须学不会的限制。

    如果有一个人u没有学会,就会给他周围的人v一个限制:v不能太早学会,否则吃饭的时候v就会传授给u

    所以将lim[v]定为L+1,即他们在L的时候吃饭,L+1的时候v才学会,不会传给u

    先将这种传递关系用spfa预处理

    再跑一边dij求出每个人最早在多久学会。(将u、v之间的连边关系视作u学会了传授给v来更新v)

    转移的时候是这样转移的:dis[v]=max( L,max( dis[u],lim[v] ) )

    原因: L指在这个区间内 ,dis指 u要先学会 , lim v 指在v学会的范畴内(防止出现v太早学会而将算法传给后面的人 这就是lim的作用)

    #include<bits/stdc++.h>
    using namespace std;
    #define N 200005
    #define ri register int
    #define inf 1000000007//一定要足够大 否则会错!! 
    int tot=0,to[N<<1],head[N],nex[N<<1],l[N<<1],r[N<<1],dis[N],vis[N],lim[N],x[N],n,m;
    struct node1 { int a,b,l,r; }e[N];
    struct node2 { int s,dis; };
    void add(int a,int b,int ll,int rr)
    {
        to[++tot]=b; nex[tot]=head[a]; head[a]=tot; l[tot]=ll; r[tot]=rr;
        to[++tot]=a; nex[tot]=head[b]; head[b]=tot; l[tot]=ll; r[tot]=rr;
    }
    bool operator < (const node2 &a,const node2 &b) { return a.dis>b.dis; }
    void dij()//求u能传给v的dis 
    {
        priority_queue <node2> q;
        for(ri i=1;i<=n;++i) dis[i]=inf,vis[i]=0;//dis表示每个人在lim限制下最晚学会的时间 
        dis[1]=0; q.push((node2){1,0});
        while(!q.empty()){
            int u=q.top().s; q.pop();
            if(vis[u]) continue;
            vis[u]=1;
            for(ri i=head[u];i;i=nex[i]){
                int v=to[i],mn=l[i],mx=r[i];
                int tmp=max(mn,max(dis[u],lim[v]));//u要能在这一次吃饭就传给v 最早吃饭的时间就是从这几个中取max 
                //mn在这个区间内 dis u要先学会  lim v 在v学会的范畴内(防止出现v太早学会而将算法传给后面的人 这就是lim的作用) 
                if(dis[v]>tmp && tmp<=mx){//能更新 并且在范围里面 
                    dis[v]=tmp;
                    if(!vis[v]) q.push((node2){v,dis[v]});
                }
            }
        }
    }
    queue<int> q;
    void spfa()//求在有lim限制下 u不能传给v的lim 
    {
        while(!q.empty()){
            int u=q.front(); q.pop(); vis[u]=0;
            for(ri i=head[u];i;i=nex[i]){
                int v=to[i],mn=l[i],mx=r[i];
                if(lim[v]<mn+1 && lim[u]>mx){
                //如果说u应该在后面才学会 那么v又在吃饭前学会 说明是矛盾的 所以要将v改成吃完饭后学会 贪心的说就是L+1 
                    lim[v]=mn+1;
                    if(!vis[v]) q.push(v),vis[v]=1;
                }
            }
        }
    }
    void print() { printf("Impossible
    "); exit(0); }
    int main()
    {
        freopen("lunch.in","r",stdin);
        freopen("lunch.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(ri i=1;i<=m;++i) scanf("%d%d%d%d",&e[i].a,&e[i].b,&e[i].l,&e[i].r),add(e[i].a,e[i].b,e[i].l,e[i].r);
        for(ri i=1;i<=n;++i){
            scanf("%d",&x[i]);
            if(x[i]==-1) lim[i]=inf,vis[i]=1,q.push(i);//先将lim的限制传递下去 将与他相连的点的lim 赋成 L+1 
            else vis[i]=0;
        }
        spfa();
        dij();
        for(ri i=1;i<=n;++i) if( x[i]==1 && dis[i]==inf) print();//如果说是必须学会 但没有学会 是不合法的 
        for(ri i=1;i<=m;++i){
            int u=e[i].a, v=e[i].b;
            if(x[u]==-1 && dis[v]<=e[i].l) print();//如果说是某一个不能学会 但是却学会了 就是不合法的 
            if(x[v]==-1 && dis[u]<=e[i].l) print(); 
        }
        
        for(ri i=1;i<=m;++i){
            int u=e[i].a, v=e[i].b;
            if(x[u]==-1 || x[v]==-1) printf("%d
    ",e[i].l);
            //如果他们最早不是通过这一次学会的话 这一次就可以直接贪心地直接从l时间吃饭 
            else printf("%d
    ",max(dis[u],dis[v])>e[i].r ? e[i].l : max(e[i].l,max(dis[u],dis[v])) );
            //否则就要通过这一次学会 取max是因为要u比v先学会 才能在吃饭的时候传给v 
        }
        return 0;
    }
    /*
    4 3
    1 2 1 2
    2 3 1 2
    2 4 1 2
    1 0 1 -1
    */
    View Code
  • 相关阅读:
    线段树
    5709 01背包
    JavaEE Tutorials (19)
    洛谷 P3385 【模板】负环
    洛谷 P3388 【模板】割点(割顶)
    洛谷 P3387 【模板】缩点
    洛谷 P3386 【模板】二分图匹配
    洛谷 P3371 【模板】单源最短路径
    洛谷 P3370 【模板】字符串哈希
    洛谷 P3366 【模板】最小生成树
  • 原文地址:https://www.cnblogs.com/mowanying/p/11794012.html
Copyright © 2011-2022 走看看