机房测试:lunch(贪心+最短路)

蜗牛慢爬的李成广 2019-11-04

题目:

机房测试:lunch(贪心+最短路)

机房测试: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\n"); 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\n",e[i].l);
        //如果他们最早不是通过这一次学会的话 这一次就可以直接贪心地直接从l时间吃饭 
        else printf("%d\n",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
*/

相关推荐