「NOIP模拟赛」小奇回地球
原题目名:时间与空间之旅
2015.9.13日hzwer重制了题面与数据
「题目背景」
开学了,小奇在回地球的路上,遇到了一个棘手的问题。
「问题描述」
简单来说,它要从标号为1的星球到标号为n的星球,某一些星球之间有航线。由于超时空隧道的存在,从一个星球到另一个星球时间可能会倒流,而且,从星球a到b耗费的时间和星球b到a耗费的时间不一定相同。
宇宙法规定:“禁止在出发时间前到达目的地。”
每艘飞船上都有速度调节装置,可以调节飞行的时间。其功能可以使得整次航程中所有两星球间的飞行时间增加或减少相同的整数值。你的任务是帮助它调整速度调节器,找出一条最短时间到达目的地的路径。
「输入格式」
输入文件包含多组数据,第1个数为T,表示数据组数。
对于每组数据,输入第1行为两个正整数n,m,为星球的个数和星球间的路线数。接下来m行,每行三个整数i,j和t,表示由星球i到星球j飞行的时间为t。由i到j最多只会有一条飞行线路。
「输出格式」
输出文件共T行,每组数据输出一行。
如果可以通过调节速度调节器完成任务,则输出一个非负整数,表示由星球1到星球n的最短时间。(注意最短时间要大于或者等于0)。
如果不能由星球1到达星球n,则输出-1。
「样例输入」
1
4 5
1 2 1
1 3 1
2 3 -3
3 1 1
3 4 1
「样例输出」
2
「样例解释」
把速度控制器的值设为1,相当于每个时间值加1,得到的最短路径为1→2→3→4,所需时间为2+(-2)+2=2。
「数据范围」
1,2号测试点,保证所有星球出度不超过1
3,4号测试点,n<=10
5,6号测试点,-100<=t<=100
对于100%的数据T<=10,n<=100,m<=n*(n-1),-100000<=t<=100000
数据随机和构造结合生成
题解
题意是将有向图的所有边权加上一个值,使得1-n的最短路大于等于0且尽量小
1,2号测试点是n元环,即1-n只有1条路径,直接求出路径长度和经过边数,简单计算得到答案
1号测试点中还有-1的情况
3,4号测试点n<=10,各种暴力通过
5,6号测试点-100<=t<=100
枚举答案,然后判断图中最短路是否存在,即1-n的路径上是否有负环存在。可以先用floyd传递闭包或者dfs将和1,n不连通的结点从图中去掉,然后用spfa算法来判负环。
复杂度上界为T*t*n^2*m
对于剩下的测试点,上述算法无法承受,考虑对其进行优化
枚举答案可以改成二分,负环可以用深搜版的spfa判(n*m),最终复杂度上界为T*logt*n*m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
#include<iostream> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #define pa pair<int,int> #define inf 1000000000 using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int T,n,m,cnt,ans,mid; int last[105],q[105],d[105]; bool mark[105],con[105]; struct data{int to,next,v;}e[200005]; void insert(int u,int v,int w) { e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;e[cnt].v=w; } bool dfs(int x) { mark[x]=1; for(int i=last[x];i;i=e[i].next) if(d[x]+e[i].v+mid<d[e[i].to]&&con[e[i].to]) { if(mark[e[i].to])return 1; d[e[i].to]=d[x]+e[i].v+mid; if(dfs(e[i].to))return 1; } mark[x]=0; return 0; } void spfa() { for(int i=1;i<=n;i++)d[i]=inf; int head=0,tail=1; q[0]=1;mark[1]=1;d[1]=0; while(head!=tail) { int now=q[head];head++;if(head==100)head=0; for(int i=last[now];i;i=e[i].next) if(d[now]+e[i].v+mid<d[e[i].to]&&con[e[i].to]) { d[e[i].to]=d[now]+e[i].v+mid; if(!mark[e[i].to]) { mark[e[i].to]=1; q[tail]=e[i].to;tail++; if(tail==100)tail=0; } } mark[now]=0; } } void dfscon(int x) { mark[x]=1; for(int i=last[x];i;i=e[i].next) if(!mark[e[i].to]) dfscon(e[i].to); } bool jud() { for(int i=1;i<=n;i++) if(con[i]) { for(int j=1;j<=n;j++)d[j]=inf; memset(mark,0,sizeof(mark)); if(dfs(i))return 0; } spfa(); if(d[n]>=0&&d[n]!=inf)return 1; return 0; } int main() { T=read(); while(T--) { memset(con,1,sizeof(con)); memset(last,0,sizeof(last)); cnt=0;ans=-1; n=read();m=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(),w=read(); insert(u,v,w); } memset(mark,0,sizeof(mark)); dfscon(1); for(int i=1;i<=n;i++)if(!mark[i])con[i]=0; for(int i=1;i<=n;i++) if(con[i]) { memset(mark,0,sizeof(mark)); dfscon(i); if(!mark[n])con[i]=0; } int l=-100000,r=100000; while(l<=r) { mid=(l+r)>>1; if(jud())ans=d[n],r=mid-1; else l=mid+1; } printf("%d\n",ans); } return 0; } |
样例为什么不能走1–>3–>4,再都减去1,变成ANS=0啊?
题意是,将图的边权都加上k以后,从1走最短路到n
如果将所有边权都减去1以后,图中出现了负环,就不存在最短路了
原题在那个OJ上啊?