最短路练习题

Til the Cows Come Home

  • 模板题,注意输入顺序是先 t t t n n n
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1005;
struct st{
	int ID;
	int val;
	friend bool operator<(st x, st y){
		return x.val > y.val;
	}
};
struct Edge{
	int next;
	int to;
	int val;
}edge[200000];
int cnt;
int head[MAXN];
int vis[MAXN];
int dis[MAXN];
void Add_Edge(int u, int v, int w){
	edge[cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].val = w;
	head[u] = cnt++;	
}
void dijstra(int s){
	priority_queue<st> q;
	st now;
	now.ID = s;
	dis[s] = 0;
	now.val = dis[s];
	q.push(now);
	while(!q.empty()){
		st u = q.top();
		q.pop();
		if(vis[u.ID]) continue;
		vis[u.ID] = 1;
		for(int i=head[u.ID];i!=-1;i=edge[i].next){
			if(dis[u.ID] + edge[i].val < dis[edge[i].to] && !vis[edge[i].to]){
				dis[edge[i].to] = dis[u.ID] + edge[i].val;
				now.ID = edge[i].to;
				now.val = dis[edge[i].to];
				q.push(now);
			}
		}
	}
}
int main(){
	int t, n, u, v, w;
	cin >> t >> n;
	memset(dis, 0x3f, sizeof dis);
	memset(head, -1, sizeof head);	
	for(int i=0;i<t;i++){
		cin >> u >> v >> w; 
		Add_Edge(u, v, w);
		Add_Edge(v, u, w);
	}
	dijstra(1);
	cout << dis[n];
	return 0;
}

Frogger

  • 题意:有两只青蛙,分别在一号和二号位置,现在一号青蛙想到二号青蛙的位置上去,借助其他位置,问到达二号位置的过程中每次跳跃的最短跳跃距离
  • 数据范围比较小,求出所有石头两两之间距离,更改 d i j s t r a dijstra dijstra算法的判断条件为取当前最短距离和需要跳跃的距离之间的最大值
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 1005;
struct st{
	int ID;
	double val;
	friend bool operator<(st x, st y){
		return x.val > y.val;
	}
};
struct Edge{
	int next;
	int to;
	double val;
}edge[200000];
struct NODE{
	int x, y;
}node[MAXN];
int cnt;
int head[MAXN];
int vis[MAXN];
double dis[MAXN];
void Add_Edge(int u, int v, double w){
	edge[cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].val = w;
	head[u] = cnt++;	
}
void dijstra(int s){
	priority_queue<st> q;
	st now;
	now.ID = s;
	dis[s] = 0;
	now.val = dis[s];
	q.push(now);
	while(!q.empty()){
		st u = q.top();
		q.pop();
		if(vis[u.ID]) continue;
		vis[u.ID] = 1;
		for(int i=head[u.ID];i!=-1;i=edge[i].next){
			if(max(dis[u.ID], edge[i].val) < dis[edge[i].to] && !vis[edge[i].to]){
				dis[edge[i].to] = max(dis[u.ID], edge[i].val);
				now.ID = edge[i].to;
				now.val = dis[edge[i].to];
				q.push(now);
			}
		}
	}
}
int main(){
	int t, n, u, v, x, y;
	double w;
	int num = 1;
	while(cin >> n && n){
		for(int i=1;i<=n;i++) dis[i] = 999999999.0;
		memset(head, -1, sizeof head);
		memset(vis, 0, sizeof vis);
		for(int i=1;i<=n;i++){
			cin >> x >> y;
			node[i].x = x;
			node[i].y = y;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				Add_Edge(i, j, sqrt((node[i].x - node[j].x) * (node[i].x - node[j].x) + (node[i].y - node[j].y) * (node[i].y - node[j].y)));
				Add_Edge(j, i, sqrt((node[i].x - node[j].x) * (node[i].x - node[j].x) + (node[i].y - node[j].y) * (node[i].y - node[j].y)));
			}
		}
		dijstra(1);
		printf("Scenario #%d\n", num++);
		printf("Frog Distance = %.3f\n\n", dis[2]);
	}
	return 0;
}

Heavy Transportation

  • 给出每条路的最大允许载重量,要求从位置 1 1 1到位置 n n n所经过的路径中最大的允许重量,如果1到2最大载重量为3,2到3最大载重量为5,1到3最大载重量为4,那么因为有两条路径能够到达位置n,所以经过2的路径最多只能载重3;而直接到3的最大载重量为4,综合这两条路径,所以总的最大载重量为4
  • 同样是最短路的变形问题,在每一个点扩展的过程中每次找从起点到当前点载重量的最小值,这是这个点的最大载重量,最后需要取整体最大的载重量,优先级应为从大到小
#include <iostream>
#include <queue>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
const int MAXN = 1e3 + 100;
struct st{
    int ID;
    int val;
    bool operator<(st x)const {
        return val < x.val;
    }
};
struct Edge{
    int next;
    int to;
    int val;
}edge[200000];
int head[MAXN];
int vis[MAXN];
int dis[MAXN];
int cnt;
void Add_Edge(int u, int v, int w){
    edge[cnt].to = v;
    edge[cnt].next = head[u];
    edge[cnt].val = w;
    head[u] = cnt++;
}
void dijstra(int s){
    priority_queue<st> q;
    st now;
    dis[s] = 0x3f3f3f3f;
    now.ID = s;
    now.val = dis[s];
    q.push(now);
    while(!q.empty()){
        st u = q.top();
        q.pop();
        if(vis[u.ID]) continue;
        vis[u.ID] = 1;
        for(int i=head[u.ID];i != -1;i = edge[i].next){
            if(!vis[edge[i].to] && min(dis[u.ID], edge[i].val) > dis[edge[i].to]){
                dis[edge[i].to] = min(dis[u.ID], edge[i].val);
                now.ID = edge[i].to;
                now.val = dis[edge[i].to];
                q.push(now);
            }
        }
    }
}
int main(){
    int t, n, m, u, v, w;
    scanf("%d", &t);
    for(int j=1;j<=t;j++){
        memset(head, -1, sizeof head);
        memset(vis, 0, sizeof vis);
        memset(dis, -0x3f, sizeof dis);
        cnt = 0;
        scanf("%d%d", &n, &m);
        for(int i=0;i<m;i++){
            scanf("%d%d%d", &u, &v, &w);
            Add_Edge(u, v, w);
            Add_Edge(v, u, w);
        }
        dijstra(1);
        printf("Scenario #%d:\n", j);
        printf("%d\n\n", dis[n]);
    }
    return 0;
}

Silver Cow Party

  • n n n头奶牛,分别在 1 − n 1-n 1n号农场,现在在 X X X号农场要开一个晚会,道路是有向的,问这些奶牛到达农场再回来要经过的最短路
  • 数组开小一些防止 m e m s e t memset memset时间过慢,直接跑 n n n次最短路,最后分别求奶牛的最短路取最大值即可
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 100;
struct st{
	int ID;
	int val;
	bool operator<(st x)const {
		return val > x.val;
	}
};
struct Edge{
	int next;
	int to;
	int val;
}edge[200000];
int head[MAXN];
int vis[MAXN];
int dis[MAXN];
int cnt;
void Add_Edge(int u, int v, int w){
	edge[cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].val = w;
	head[u] = cnt++;
}
map<int, int> mp;
void dijstra(int s){
	priority_queue<st> q;
	st now;
	dis[s] = 0;
	now.ID = s;
	now.val = dis[s];
	q.push(now);
	while(!q.empty()){
		st u = q.top();
		q.pop();
		if(vis[u.ID]) continue;
		vis[u.ID] = 1;
		for(int i=head[u.ID];i!=-1;i=edge[i].next){
			if(!vis[edge[i].to] && dis[u.ID] + edge[i].val < dis[edge[i].to]){
				dis[edge[i].to] = dis[u.ID] + edge[i].val;
				now.ID = edge[i].to;
				now.val = dis[edge[i].to];
				q.push(now);
			}
		}
	}
} 
int main(){
	int n, m, x, u, v, w;
	scanf("%d%d%d", &n, &m, &x);
	memset(head, -1, sizeof head);
	memset(dis, 0x3f, sizeof dis);	
	while(m--){
		scanf("%d%d%d", &u, &v, &w);
		Add_Edge(u, v, w);
	}
	dijstra(x);
	for(int i=1;i<=n;i++){
		mp[i] = dis[i];
	}
	int ans = -0x3f3f3f3f;
	for(int i=1;i<=n;i++){
		if(i == x) continue;
		memset(dis, 0x3f, sizeof dis);
		memset(vis, 0, sizeof vis);
		dijstra(i);
		ans = max(ans, mp[i] + dis[x]);
	}
	cout << ans;
	return 0;
}

Currency Exchange

  • 题目描述了一个情景,告诉我们用A换B的汇率和佣金,现在问经过兑换,最后需要换回原来的币种,能不能换完之后发现钱变多了
  • 问题明显是求正环的问题,使用 b e l l m a n − f o r d bellman-ford bellmanford算法求解,我们进行 ∣ V ∣ − 1 |V|-1 V1次松弛操作,记录现在的结果,然后重新跑一边 b e l l m a n − f o r d bellman-ford bellmanford,这时候如果没正环,应该是最大的情况,如果发现又变大了,只能说明图中存在正环
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
double edge1[200][200];
double edge2[200][200];
double dis[200];
bool Bellman_Ford(int n){
    double d[200];
    for(int i=1;i<=n;i++){
        d[i] = dis[i];
    }
    for(int k=1;k<n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(dis[j] < (dis[i] - edge2[i][j]) * edge1[i][j]){
                    dis[j] = (dis[i] - edge2[i][j]) * edge1[i][j];
                }
            }
        }
    }
    for(int i=1; i<=n; i++){
        if(d[i] < dis[i]) return true;
    }
    return false;
}
int main(){
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
    int n, m, s, u, x, y;
    double v;
    double rab, rba, cab, cba;
    scanf("%d%d%d%lf", &n, &m, &s, &v);
    dis[s] = v;
    while(m--){
        scanf("%d%d%lf%lf%lf%lf", &x, &y, &rab, &cab, &rba, &cba);
        edge1[x][y] = rab;
        edge1[y][x] = rba;
        edge2[x][y] = cab;
        edge2[y][x] = cba;
    }
    Bellman_Ford(n);
    if(Bellman_Ford(n)) printf("YES");
    else printf("NO");
    return 0;
}

Wormholes

  • 给出一些路径,农场之间的路径是双向的,虫洞是单向的,进入虫洞会使时间倒退,走正常的路会消耗时间,问有没有可能在从某个地方出发之前到达该地方
  • 如果把进入虫洞的时间设为负值,那么这就是一个判断有没有负环的问题,可以使用 B e l l m a n − f o r d Bellman-ford Bellmanford f l o y d floyd floyd两种方法
  • 黑书上介绍的是 f l o y d floyd floyd方法,思路是令 e d g e [ i ] [ i ] = 0 edge[i][i]=0 edge[i][i]=0,之后跑一遍 f l o y d floyd floyd,如果发现对于某个 i i i,从 i i i i i i时间为负数,说明有负环,但是时间复杂度很高,需要在 f l o y d floyd floyd加一个小小的优化,剪掉路径不存在的情况
  • 我写的朴素 b e l l m a n − f o r d bellman-ford bellmanford没通过,但是思路是清晰的
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
int edge[600][600];
bool floyd(int n){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            if(edge[i][k] != INF)
            for(int j=1;j<=n;j++){
                if(edge[i][j] > edge[i][k] + edge[k][j]){
                    edge[i][j] = edge[i][k] + edge[k][j];
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(edge[i][i] < 0) return true;
    }
    return false;
}
int main(){
    int F, n, m, w, s, e, t;
    scanf("%d", &F);
    while(F--){
        scanf("%d%d%d", &n, &m, &w);
        memset(edge, INF, sizeof edge);
        while(m--){
            scanf("%d%d%d", &s, &e, &t);
            if(t < edge[s][e]){
                edge[s][e] = edge[e][s] = t;
            }
        }
        while(w--){
            scanf("%d%d%d", &s, &e, &t);
            edge[s][e] = min(-t, edge[s][e]);
        }
        for(int i=1;i<=n;i++) edge[i][i] = 0;
        if(floyd(n)) printf("YES");
        else printf("NO");
        printf("\n");
    }
    return 0;
}

Cow Contest

  • 给出若干组比赛结果,格式为 A    B A\ \ B A  B,表示 A A A赢了 B B B,问有几个能够确定的名次
  • 如果想确定名次,必须要知道他和其他所有人的比赛结果,可以利用 f l o y d floyd floyd的递推关系,对于 A , B , C A,B,C A,B,C如果 A A A赢了 B B B,那么令 e d g e [ A ] [ B ] = 1 , e d g e [ B ] [ A ] = − 1 edge[A][B]=1,edge[B][A]=-1 edge[A][B]=1,edge[B][A]=1,如果 e d g e [ B ] [ C ] = 1 edge[B][C]=1 edge[B][C]=1那么就可以推出 e d g e [ A ] [ C ] = 1 edge[A][C]=1 edge[A][C]=1,进而推出所有人的比赛结果,最后计数
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
int edge[200][200];
void floyd(int n){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(edge[i][k] == edge[k][j] && edge[i][k] != INF){
                    edge[i][j] = edge[i][k];
                }
            }
        }
    }
}
int main(){
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
    int n, m, u, v;
    cin >> n >> m;
    memset(edge, INF, sizeof edge);
    while(m--){
        cin >> u >> v;
        edge[u][v] = 1;
        edge[v][u] = -1;
    }
    floyd(n);
    int ans = 0;
    for(int i=1;i<=n;i++){
        int num = 0;
        for(int j=1;j<=n;j++){
            if(i == j) continue;
            if(edge[i][j] != INF) num++;
        }
        if(num == n - 1) ans++;
    }
    cout << ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值