图论——最短路

Tyvj 1221 微子危机——战略

背景

№.3
Summer联盟战前兵力战略转移。

描述

Summer的兵力分布在各个星球上,现在需要把他们全部转移到某个星球上。
Summer一共拥有N个星球(1~N),你要把这N个星球上的兵力转到第M个星球上。本来每个星球之间都有星际轨道连接,但Guiolk监视了某些轨道,我们一但走上这些轨道,有可能受到他的攻击。为了安全,Summer不会走被监视的轨道。于是,只有L个星际轨道是被批准通过的。Summer的国防部想统计一下所需的最短路程(即每个星球到第M星球的最短路程总和,单位:M  PS:'M'不是米)。

输入格式

第一行有三个正整数,N,M,L(分别如题目描述)接下来L行,为被批准通行的轨道。每行有三个整数:a,b,c,表示第a个星球和第b个星球之间的轨道长cM(有重复)。

输出格式

如果所有星球上的兵力能全部集中到第M个星球,则输出: 最短路程和+“ M(s) are needed.”如果某个星球的兵力不能到达第M个星球,则输出“Sth wrong.”。

测试样例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.

备注

对于30%的数据,1≤N≤20   ,   L≤200
对于80%的数据,1≤N≤600   ,   L≤180000
对于100%的数据,1≤N≤1000   ,   1≤M≤N   ,   L≤500000,   1≤a,b≤N   ,   0≤c≤10000。
2010年广州市第二中学初二第二次测试第三题。
 
思路:
堆优化dij裸求最短路
代码:
 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 }
View Code

 

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 2
1 1
4 5
7 1
6 7

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 }
View Code

 

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 }
View Code

②自己写的,感觉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 }
View Code

 

Wikioi 3971 航班

题目描述 Description

B 国有N 座城市,其中1 号是这座国家的首都。 

 

N 座城市之间有M 趟双向航班。i 号点的转机次数定义为:从1 号点到i ,最少需要转机几 次。如果1 根本无法到达i ,那么i 点的转机次数是无穷大。 

 

由于天气原因,有些航班会被取消。 

 

一趟航班的取消是可容忍的,仅当这趟航班取消之后,2..N 每个点的转机次数不变或者只 增加了1。 

 

现在kAc 想知道,哪些航班的取消是可容忍的? 

 

如果这样的航班不存在,输出一行 “hehe”(不含引号) 

 

输入描述 Input Description

第一行两个正整数N, M 

 

接下来M 行每行两个正整数a, b。表示当前这趟航班的两端是a,b 两座城市 ,保证a 不等于b ,且同一对a, b 只会出现一次。 

 

输出描述 Output Description

若干整数,从小到大排序,表示所有的可容忍取消的航班序号。 

样例输入 Sample Input

5 6 

1 2 

1 3 

1 4 

3 4 

2 5 

3 5 

 

样例输出 Sample Output

 

数据范围及提示 Data Size & Hint

如果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 }
View Code

 

Tyvj 1176 火焰巨魔的惆怅

背景

TYVJ2月月赛第一道

巨魔家族在某天受到了其他种族的屠杀,作为一个英雄,他主动担任了断后的任务,但是,在巨魔家族整体转移过后,火焰巨魔却被困住了,他出逃的方式也只有召唤小火人这一种方式,所以请你帮助他。

描述

我们把火焰巨魔所处的位置抽象成一张有向图,他的位置就是1号点位,目的就是走到第N号点位,因为小火人会裂嘛,所以我们可以看做每走一条路,小火人的数量都会加倍,而每条路上的敌人有多强,会消耗多少小火人c[i]也会给出(c[i]为负值);当然有些时候路上也会遇到魔法泉之类的东西,这时候就可以补充一些小火人咯(c[i]为正值)。如果小火人死光了,那么火焰巨魔也就可以看做是挂了,毕竟智力型英雄就是脆啊。希望你帮助火焰巨魔用最少的初始小火人逃离这次屠杀。

输入格式

第一行两个数N(<=50000),M(<=100000)表示点位数与边数。
一下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个,到3点剩2个,到4变成5个,到5剩1个。
所以初始最少为4,更少的小火人是不足以走到5号点位的。from wsd  TYVJ月赛出题组
 
思路:
1、由于要求的是从起点到达终点最少出发的小火人,所以是一个路径寻找问题,所以要用到最短路
2、由于小火人最少是1,所以到终点后小火人数量一定是1,否则就必须要在起点派出更多小火人
3、在最短路中,目的是求长度最小,放入队列的必要条件是可以把该点长度变小,所以此题中放入的条件是可以把所需要的火人数量变小
4、从终点开始,做spfa,则由用来松弛的点走到当前节点的最小火人数设为x,则2x + g[now][i] = d[now],可知x可能不为正整数,若为浮点数则向上取整,若x≤0,则将x设为1,然后去比较d[i]进行松弛,最后d[1]便是正确答案
 
View Code
难题:长途旅行(最短路在判定问题中的应用:压缩状态)No.4
posted @ 2015-09-24 13:57  ACforever  阅读(402)  评论(0编辑  收藏  举报