机房测试: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
*/

原文地址:https://www.cnblogs.com/mowanying/p/11794012.html

时间: 2024-10-10 14:36:35

机房测试:lunch(贪心+最短路)的相关文章

机房测试1:big(贪心+Trie树)

题目: 分析: 考虑最暴力的办法:枚举选哪个数,枚举对手在哪个时间变化,然后统计答案. 对于异或这一类问题,考虑区间异或可以抵消重复区间,维护一个前缀异或和:pre[i]表示1~i的异或和,suf[i]表示i~n的异或和. 将对手的式子化简,2*x即将x向左移一位,/( 2^n )为向右移n位,+2*x ,%2^n类似. 模拟一下:12345 -> 123450 -> 123451 -> 23451 每次枚举对手要变的时间i,最后的值即为:work ( pre[i] ^ x ) ^ su

机房测试3:C++锦标赛(贪心)

题目: 分析: 首先理解题意:zyg要和每一个人都打比赛,且只有输和赢两种情况,也就是说没打赢的人最后得分要++. 我们希望zyg打赢的人尽量地少,且rp值小. 先对比分大小排序,估计一下对应排名的最小分数sc,再按rp从小到大排序,然后分情况贪心: 1.使其最终得分为sc+2: 只需要打赢前sc+2个人即可,没有其他人会影响到他. 2.最终得分为sc+1: 我们担心得分为sc的人因为没有被打赢,得分+1而排在zyg前面,所以应该先处理得分为sc的人,把他们都打赢. (同时也要打赢sc+1的人,

机房测试2:sushi(断环+贪心)

题目: 分析: 因为原序列是一个环,所以要断环为链,将序列复制一份放在后面. 显然将R移动到一块的同时,B也会在一块,所以只需要求R移动到一起的贡献即可. 枚举一个分界点,让这个点左边所有的R都向左靠,右边所有的R都向右靠.这时候一定是满足题意的. 但会发现,同一个分界点,随着断环的位置改变,统计出来的答案也会改变,所以还要枚举一个断环点. 复杂度是n^2 考虑优化: RRBBRBBBR 像这样一组数据,把将R向两边放看做是将B往中间靠,那么在第5个位置是最优的(左边移两格,右边不需要移). 当

Host1Plus主机商8个机房测试IP体验

Host1Plus商家也算是比较老的海外主机商(英国),当初进入中国市场也是比较早的,记得那时候支持支付宝的海外主机商尤其的深受用户喜欢.因为我 们大部分网友.站长最多有一个支付宝,很少有双币信用卡或者贝宝支付美元的能力.不过后来几年,HOST1PLUS商家逐渐的落寞,主要原因在于其他商家 的出现,以及他们本身营销能力和产品的策略. 尤其是提供的虚拟主机和VPS主机,价格和方案不是太符合中国用户的口味,配置比较低,而且机房较 少.不过商家应该意识到这一点,在最近2年改动挺大的,今天公司业务正好有

hihoCoder_#1092 Have Lunch Together(最短路)

#1092 : Have Lunch Together 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Everyday Littile Hi and Little Ho meet in the school cafeteria to have lunch together. The cafeteria is often so crowded that two adjacent seats are hard to find. School cafeteria can

[20191004机房测试] ZGY的早餐

ZGY 每天早上要从宿舍走路到机房,顺便从学校小卖部购买早饭,当然机智的 ZGY 一定会走最短路 学校的路可以看成一无向联通张图,图上有 n 个点,m 条边,每一个点都有一个唯一的编号 1~n 每一条边有一个边权,表示两个点之间的距离,ZGY 的宿舍在 S 点,机房在 T点,而小卖部在 H 点 现在 ZGY 想知道从宿舍经过小卖部到达机房的最短距离 不过因为在这个世界上有 Q个 ZGY,所以你必须回答 Q 个问题 很棒的数据分治题 读入里面说了会读入测试点编号-- 其实是很明显的暗示了-- 一半

ZOJ 3946 Highway Project 贪心+最短路

题目链接: http://www.icpc.moe/onlinejudge/showProblem.do?problemCode=3946 题解: 用dijkstra跑单元最短路径,如果对于顶点v,存在一系列边(ui,v)使得dis[v]最小(dis[v]表示0到v的距离).这些边能且只能选一条,那么我们自然应该选cost最小的那个边了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #inc

[20191004机房测试] C++锦标赛

有一个比赛已经有 n 个人参加,并且互相之间已经进行了比赛 每一个人都有一个得分用于最后排名 你作为第 n + 1 个参赛者,需要与之前的每一个人打一场比赛,初始得分为 0 对于每场比赛,有两个人参加,不会存在平局,胜者得分增加 1,败者得分不变 最后按照得分从高到低来排名,假设有人与你最终得分相同 那么如果那个人曾经输给了你就会排在你后面,否则会排在你的前面 如果你赢了某个人,就需要消耗相应的 RP 值 现在你可以决定赢那些人,求最少需要消耗多少 RP 值才能到前k名 50分做法:还是枚举二进

机房测试5:reverse(bfs+set 或 线段树优化建图)

题目: 分析: 首先画样例分析一下,会发现如果要求一个位置要多少次翻转,就将这个位置向与它关联的点连边(关联点指的是可能与它值互换的位置),一直连到起点为止,连边的次数即为它所需步数. 所以转换成求单源最短路,因为边权为1,可以用bfs. 但是这道题n的范围很大,刚刚的做法是n*k的,考虑优化. 法1:在建图上优化 题目要求的是区间翻转,所以也对应着相关性质:每个点连边一定是都连的奇数点或偶数点(画图可知),且这些奇数偶数点都对应着一段连续的区间. 如果可以将点向点连边优化成点向区间连边,复杂度