图论——最短路
Tyvj 1221 微子危机——战略
背景
Summer联盟战前兵力战略转移。
描述
Summer一共拥有N个星球(1~N),你要把这N个星球上的兵力转到第M个星球上。本来每个星球之间都有星际轨道连接,但Guiolk监视了某些轨道,我们一但走上这些轨道,有可能受到他的攻击。为了安全,Summer不会走被监视的轨道。于是,只有L个星际轨道是被批准通过的。Summer的国防部想统计一下所需的最短路程(即每个星球到第M星球的最短路程总和,单位:M PS:'M'不是米)。
输入格式
输出格式
测试样例1
输入
【样例输入1】
5 3 6
1 2 1
1 3 3
2 3 1
4 1 5
4 5 2
5 1 2
【样例输入2】
5 3 4
1 2 1
1 3 3
2 3 1
5 1 2
输出
【样例输出1】
13 M(s) are needed.
【样例输出2】
Sth wrong.
备注
对于80%的数据,1≤N≤600 , L≤180000
对于100%的数据,1≤N≤1000 , 1≤M≤N , L≤500000, 1≤a,b≤N , 0≤c≤10000。
2010年广州市第二中学初二第二次测试第三题。
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<vector> 6 #include<queue> 7 #include<algorithm> 8 #define mx 1001 9 10 using namespace std; 11 struct orz 12 { 13 int d,p; 14 friend bool operator <(orz a,orz b) {return a.d>b.d;}//堆和set里面都只有小于号,所以要用小根堆的话要将<重定向为> 15 }; 16 struct Edge{ 17 int to; 18 int w; 19 }; 20 priority_queue < orz > ss; 21 int flag = 0,v[mx],d[mx],n,m,l; 22 vector<Edge> edge[mx]; 23 void input(){ 24 cin>>n>>m>>l; 25 int u,v,wei; 26 Edge test; 27 for(int i = 1;i <= l;i++){ 28 scanf("%d%d%d",&u,&v,&wei); 29 test.to = v; 30 test.w = wei; 31 edge[u].push_back(test); 32 test.to = u; 33 edge[v].push_back(test); 34 } 35 for(int i = 0;i < mx;i++) d[i] = 1000000000; 36 } 37 void dij(int s) 38 { 39 40 d[s]=0; 41 orz tmp; 42 tmp.d=0,tmp.p=s; 43 ss.push(tmp); 44 flag++; 45 int x,dd; 46 Edge j; 47 while (!ss.empty())//不能只做n次,要一直做到堆空 48 { 49 tmp=ss.top(); 50 ss.pop(); 51 x=tmp.p,dd=tmp.d; 52 if (v[x]==flag) continue;//这里一定要判断!!! 53 v[x]=flag; 54 for (int i = 0;i < edge[x].size();i++){ 55 56 j = edge[x][i]; 57 if (d[j.to]>dd+j.w) 58 { 59 d[j.to]=dd+j.w; 60 tmp.d=dd+j.w,tmp.p=j.to; 61 ss.push(tmp); 62 } 63 } 64 65 } 66 } 67 int main(){ 68 input(); 69 dij(m); 70 int ans = 0; 71 for(int i = 1;i <= n;i++){ 72 if(i == m) continue; 73 if(d[i] >= 1000000000){ 74 cout<<"Sth wrong."<<endl; 75 return 0; 76 } 77 ans+=d[i]; 78 } 79 cout<<ans<<" M(s) are needed."<<endl; 80 return 0; 81 }
2015山东信息学夏令营 Day5T3 路径
问题描述:
给定平面上的n个点,定义(x1,y1)到(x2,y2)的费用为min(|x1-x2|,|y1-y2|),求从1号点走到n号点的最小费用。
输入:
第一行包含一个正整数n,表示点数。
接下来n行,每行包含两个整数x[i],y[i](0<=x[i],y[i]<=10^9),依次表示每个点的坐标。
输出:
一个整数,即最小费用。
输入输出样例:
path.in |
path.out |
5 |
2
|
数据范围:
对于30%的数据,1<=n<=100;
对于60%的数据,1<=n<=1000;
对于全部的数据,1<=n<=200000;
思路:
1、60分直接最短路
2、对于坐标系中连续的三个点,(x1,y1),(x2,y2),(x3,y3),总有min(x2-x1,y2-y1) + min(x3-x2,y3-y2) ≤ min(x3 - x1,y3 - y1),所以在建图的时候,可以按x,y分别排一遍序,然后,相邻的点建边,这样边数为2*n,再用堆dij就可以了
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 #include<queue> 8 #define maxn 200005 9 #define inf ~0U>>1 10 using namespace std; 11 struct point{ 12 int x; 13 int y; 14 int pos; 15 }; 16 struct orz{ 17 int p; 18 int d; 19 friend bool operator < (orz a,orz b){ 20 return a.d > b.d; 21 } 22 }; 23 struct edge{ 24 int v; 25 int w; 26 }; 27 priority_queue< orz > ss; 28 vector<edge> g[maxn]; 29 point pt[maxn]; 30 int flag = 0,v[maxn],d[maxn],n; 31 bool cmpa(point a,point b){ 32 return a.x < b.x; 33 } 34 bool cmpb(point a,point b){ 35 return a.y < b.y; 36 } 37 void init(){ 38 cin>>n; 39 int tx,ty; 40 for(int i = 1;i <= n;i++){ 41 scanf("%d%d",&tx,&ty); 42 pt[i].x = tx; 43 pt[i].y = ty; 44 pt[i].pos = i; 45 } 46 for(int i = 1;i <= n;i++) d[i] = inf; 47 sort(pt+1,pt+1+n,cmpa); 48 edge tmp; 49 for(int i = 1;i < n;i++){ 50 tmp.w = pt[i+1].x - pt[i].x; 51 tmp.v = pt[i + 1].pos; 52 g[pt[i].pos].push_back(tmp); 53 tmp.v = pt[i].pos; 54 g[pt[i+1].pos].push_back(tmp); 55 } 56 sort(pt+1,pt+1+n,cmpb); 57 for(int i = 1;i < n;i++){ 58 tmp.w = pt[i+1].y - pt[i].y; 59 tmp.v = pt[i + 1].pos; 60 g[pt[i].pos].push_back(tmp); 61 tmp.v = pt[i].pos; 62 g[pt[i+1].pos].push_back(tmp); 63 } 64 } 65 void dij(){ 66 orz tmp; 67 d[1] = 0; 68 tmp.p = 1; 69 tmp.d = 0; 70 ss.push(tmp); 71 flag++; 72 int to,wei,x,dd; 73 edge j; 74 while(!ss.empty()){ 75 tmp = ss.top(); 76 ss.pop(); 77 x = tmp.p; 78 dd = tmp.d; 79 if(v[x] == flag) continue; 80 v[x] = flag; 81 for(int i = 0;i < g[x].size();i++){ 82 j = g[x][i]; 83 to = j.v; 84 wei = j.w; 85 if(d[to] > dd + wei){ 86 d[to] = dd + wei; 87 tmp.d = dd + wei; 88 tmp.p = to; 89 ss.push(tmp); 90 } 91 } 92 } 93 cout<<d[n]; 94 } 95 int main(){ 96 97 init(); 98 dij(); 99 return 0; 100 }
2015山东信息学夏令营 Day4T3 生产
【题目描述】
工厂为了生产一种复杂的产品,给各个生产部门制定了详细的生产计划。那么,就经常会有生产部门要把产品送到另一个生产部门作为原料。这是一个注重产品质量的工厂,所以每当有产品要从A部门运到B部门时,都要先从A部门送到质量检验处,检验合格后再从质量检验处运到B部门。
有些部门之间有传送带连接,厂长想知道每次将产品从一个部门运送到另一个部门最少需要多长时间。
【输入格式】
第一行两个整数n、m,n表示部门数量,m表示传送带数量。出于方便,1号部门是质量检验处。
接下来m行,每行三个整数u、v、w,表示有一条从u部门到v部门的传送带,传送过去需要w个单位时间。注意传送带是单向的。
接下来一个整数q,表示有q次运送。
接下来q行,每行两个数a、b,表示这一次要将产品从a部门运送到b部门。
【输出格式】
输出q行,每行一个整数,表示这次运送最少需要的时间。若没有传送方案,输出-1。
【样例输入】
5 5
1 2 3
1 3 5
4 1 7
5 4 1
5 3 1
3
4 2
5 3
2 3
【样例输出】
10
13
-1
【数据规模与约定】
30%的数据,n≤100,m≤500,w=1
60%的数据,n≤100,m≤5000
另20%的数据,q=1
100%的数据,2≤n≤3000,m≤100000,2≤a,b≤n,
q≤100000,1≤u,v≤n,1≤w≤10000
有些部门之间可能有多条传送带。
工厂的员工都非常尽职尽责,他们的认真和热情决定了产品的完美,所以不必考虑产品不合格的情况。
思路:
1、最短路,A到1再到B的距离,等于1到B的距离加上1到A的距离
2、存者正反两个图,在反图上求1到A距离,在正图上求1到B的距离
代码:
①官方标程(spfa + 链式前向星)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 3050; 6 const int M = 500050; 7 const int inf = 987654321; 8 int n,m,q; 9 struct Edge 10 { 11 int u,v,w; 12 }xu[M]; 13 int point[N],to[M],next[M],val[M]; 14 int dui[N],mina[N],minb[N],top,tail; 15 bool indui[N]; 16 void MakeMinLen(int x[]) 17 { 18 int i; 19 dui[1]=1; 20 top=0;tail=1; 21 indui[1]=1; 22 for(i=1;i<=n;i++) 23 x[i]=inf; 24 x[1]=0; 25 while(top^tail) 26 { 27 top++; 28 if(top==N) 29 top=0; 30 int now=dui[top]; 31 int then=point[now]; 32 indui[now]=0; 33 while(then) 34 { 35 int tox=to[then]; 36 if(x[tox]>x[now]+val[then]) 37 { 38 x[tox]=x[now]+val[then]; 39 if(!indui[tox]) 40 { 41 indui[tox]=1; 42 tail++; 43 if(tail==N) 44 tail=0; 45 dui[tail]=tox; 46 } 47 } 48 then=next[then]; 49 } 50 } 51 } 52 void InitGraph() 53 { 54 int i; 55 scanf("%d%d",&n,&m); 56 for(i=1;i<=m;i++) 57 scanf("%d%d%d",&xu[i].u,&xu[i].v,&xu[i].w); 58 memset(point,0,sizeof point); 59 for(i=1;i<=m;i++) 60 { 61 next[i]=point[xu[i].v]; 62 point[xu[i].v]=i; 63 to[i]=xu[i].u; 64 val[i]=xu[i].w; 65 } 66 MakeMinLen(mina); 67 memset(point,0,sizeof point); 68 for(i=1;i<=m;i++) 69 { 70 next[i]=point[xu[i].u]; 71 point[xu[i].u]=i; 72 to[i]=xu[i].v; 73 val[i]=xu[i].w; 74 } 75 MakeMinLen(minb); 76 } 77 void MakeAns() 78 { 79 scanf("%d",&q); 80 while(q--) 81 { 82 int a,b; 83 scanf("%d%d",&a,&b); 84 int res=mina[a]+minb[b]; 85 if(res>=inf) 86 res=-1; 87 printf("%d\n",res); 88 } 89 } 90 int main() 91 { 92 freopen("data.in","r",stdin); 93 freopen("data.out","w",stdout); 94 InitGraph(); 95 MakeAns(); 96 return 0; 97 }
②自己写的,感觉dij快一点,一测发现比标程慢不少TAT
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 #include<queue> 8 #define inf ~0U>>2 9 #define maxn 3005 10 using namespace std; 11 struct orz{ 12 int d; 13 int p; 14 friend bool operator < (orz a,orz b){ 15 return a.d > b.d; 16 } 17 }; 18 struct edge{ 19 int v; 20 int w; 21 }; 22 priority_queue< orz > ss; 23 vector<edge> g[maxn],op[maxn]; 24 int n,m,q,flag = 0,v[maxn],d[maxn],opd[maxn]; 25 void init(){ 26 cin>>n>>m; 27 int u,v,w; 28 edge tmp; 29 for(int i = 1;i <= m;i++){ 30 scanf("%d%d%d",&u,&v,&w); 31 tmp.v = v; 32 tmp.w = w; 33 g[u].push_back(tmp); 34 tmp.v = u; 35 op[v].push_back(tmp); 36 } 37 cin>>q; 38 for(int i = 1;i <= n;i++) d[i] = opd[i] = inf; 39 } 40 void dij(){ 41 orz tmp; 42 tmp.d = 0; 43 tmp.p = 1; 44 opd[1] = 0; 45 ss.push(tmp); 46 flag++; 47 int x,dd,to,wei; 48 edge j; 49 while(!ss.empty()){ 50 tmp = ss.top(); 51 ss.pop(); 52 x = tmp.p; 53 dd = tmp.d; 54 if(v[x] == flag) continue; 55 v[x] = flag; 56 for(int i = 0;i < g[x].size();i++){ 57 j = g[x][i]; 58 to = j.v; 59 wei = j.w; 60 if(d[to] > dd + wei){ 61 d[to] = dd + wei; 62 tmp.d = dd + wei; 63 tmp.p = to; 64 ss.push(tmp); 65 } 66 } 67 } 68 } 69 void opdij(){ 70 orz tmp; 71 tmp.d = 0; 72 tmp.p = 1; 73 opd[1] = 0; 74 ss.push(tmp); 75 flag++; 76 int x,dd,to,wei; 77 edge j; 78 while(!ss.empty()){ 79 tmp = ss.top(); 80 ss.pop(); 81 x = tmp.p; 82 dd = tmp.d; 83 if(v[x] == flag) continue; 84 v[x] = flag; 85 for(int i = 0;i < op[x].size();i++){ 86 j = op[x][i]; 87 to = j.v; 88 wei = j.w; 89 if(opd[to] > dd + wei){ 90 opd[to] = dd + wei; 91 tmp.d = dd + wei; 92 tmp.p = to; 93 ss.push(tmp); 94 } 95 } 96 } 97 } 98 void ask(){ 99 int u,v; 100 long long dn; 101 for(int i = 1;i <= q;i++){ 102 scanf("%d%d",&u,&v); 103 dn = opd[u] + d[v]; 104 if(dn >= inf) cout<<-1<<endl; 105 else cout<<dn<<endl; 106 } 107 } 108 int main(){ 109 freopen("data.in","r",stdin); 110 freopen("data.out","w",stdout); 111 init(); 112 dij(); 113 opdij(); 114 ask(); 115 return 0; 116 }
Wikioi 3971 航班
B 国有N 座城市,其中1 号是这座国家的首都。
N 座城市之间有M 趟双向航班。i 号点的转机次数定义为:从1 号点到i ,最少需要转机几 次。如果1 根本无法到达i ,那么i 点的转机次数是无穷大。
由于天气原因,有些航班会被取消。
一趟航班的取消是可容忍的,仅当这趟航班取消之后,2..N 每个点的转机次数不变或者只 增加了1。
现在kAc 想知道,哪些航班的取消是可容忍的?
如果这样的航班不存在,输出一行 “hehe”(不含引号)
第一行两个正整数N, M
接下来M 行每行两个正整数a, b。表示当前这趟航班的两端是a,b 两座城市 ,保证a 不等于b ,且同一对a, b 只会出现一次。
若干整数,从小到大排序,表示所有的可容忍取消的航班序号。
5 6
1 2
1 3
1 4
3 4
2 5
3 5
2
3
4
5
6
如果1、2 两座城市间的航班被取消,2 号城市到首都原本需要0 次转机(有直达飞机) ,现
在需要先到5 ,再到3 ,再到1 ,转机2 次。这是不可忍受的。
对于40% :N, M<=500
对于70% :N<=500 M <= 50000
对于100% :N, M<=200000
保证初始给定图中所有点的转机次数不是无穷大。
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 #include <queue> 7 using namespace std; 8 9 #define fr(x, E) for (__typeof(E.begin()) x = E.begin(); x != E.end(); x++) 10 //first ,second,指的是vector中定义的第几个元素 11 #define FR first 12 #define SC second 13 #define MP make_pair 14 #define PB push_back 15 #define PII pair<int, int> 16 #define LL long long 17 #define ALL(x) x.begin(), x.end() 18 #define SZ(x) ((int)(x).size()) 19 const int MAXN = 1000001; 20 vector<PII> e[MAXN];//存储边,和第航线序号 21 int n, m; 22 int from[MAXN], vis[MAXN], dis[MAXN]; 23 bool isok[MAXN]; 24 int main() 25 { 26 freopen("flight2.in", "r", stdin); freopen("flight.out", "w", stdout); 27 scanf("%d%d", &n, &m); 28 for (int i = 1; i <= m; i++){ 29 int a, b; 30 scanf("%d%d", &a, &b); 31 e[a].PB(MP(b, i)); e[b].PB(MP(a, i)); 32 } 33 static queue<int> Q;//宽搜各个顶点到首都的转机次数。 34 Q.push(1); vis[1] = true;//首都1入队列; 35 while(SZ(Q)){//队列非空 36 int x = Q.front(); Q.pop(); 37 fr(it, e[x])//和队首顶点关联的每个顶点 38 if (!vis[it->FR]){//如果没入队列则入队列 39 vis[it->FR] = true; 40 dis[it->FR] = dis[x] + 1; 41 from[it->FR] = it->SC;//记录最短路径航线 42 Q.push(it->FR); 43 } 44 } 45 bool ok = true; 46 for (int i = 2; i <= n; i++){ 47 bool nok = true; //假设对应最短路不可以替换 48 fr(it, e[i])//遍历顶点i的每个关联顶点 , 49 50 //如果存在不在最短路径上,而且距离不超过当前距离的代替顶点 , 51 if (it->SC != from[i] && dis[it->FR] <= dis[i]) 52 nok = false;//那么,此顶点对应的最短路径这条航线是可以替换的。 53 isok[from[i]] = nok; 54 } 55 bool ex = false; 56 for (int i = 1; i <= m; i++) if (!isok[i]){ 57 printf("%d\n", i); ex = true; 58 } 59 if (!ex) puts("hehe"); 60 }
Tyvj 1176 火焰巨魔的惆怅
背景
巨魔家族在某天受到了其他种族的屠杀,作为一个英雄,他主动担任了断后的任务,但是,在巨魔家族整体转移过后,火焰巨魔却被困住了,他出逃的方式也只有召唤小火人这一种方式,所以请你帮助他。
描述
输入格式
一下M行,每行三个数a,b,c表示a,b两点间的边权是c(|c|<=10000)
输出格式
测试样例1
输入
5 4
1 2 -3
1 3 -6
3 4 1
4 5 -9
输出
4
备注
所以初始最少为4,更少的小火人是不足以走到5号点位的。from wsd TYVJ月赛出题组