最短路题单练习

1.Frogger(最大路径最小值)

题目链接

 题目大意:求所有路径中的最长的边,在这些最长边中找到最小值并输出

思路:我们可以改变dijkstra的dis数组的含义,即所有点到点v的路径上的最小值,例如现在有一条路径为 u->v,我们可以用 dis[u] 和 len(u->v)的长度取最大,再用此时的得到的值,与dis[v]取最小

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
 
using namespace std;
 
const int maxn = 220;
 
const double INF = 1e60;
 
int n;
double G[maxn][maxn], d[maxn];
 
struct Pos {
    int x, y;
}num[maxn];
 
double calc(int i, int j) {
    return (sqrt(pow((double)num[i].x-num[j].x, 2)+pow((double)num[i].y-num[j].y, 2)));
}
 
double dijkstra() {
    bool vis[maxn];
    
    memset(vis, false, sizeof(vis));
 
    for(int i=0; i<n; i++) {
        d[i] = G[0][i];
    }
 
    d[0] = 0;
    vis[0] = true;
 
    for(int i=0; i<n-1; i++) {
        double m = INF; int x;
        for(int y=0; y<n; y++) if(!vis[y] && m >= d[y]) m = d[x=y];
        vis[x] = true;
        for(int y=0; y<n; y++){
            if(!vis[y]) {
                double maxx = max(d[x], G[x][y]);
                if(d[y] > maxx) d[y] = maxx;
            }
        }
    }
    
    return d[1];
}
 
int main() {
    int cnt = 0;
    while(scanf("%d", &n) == 1) {
        if(n == 0) break;
 
        for(int i=0; i<n; i++) scanf("%d%d", &num[i].x, &num[i].y);
 
        for(int i=0; i<n; i++) {
            for(int j=0; j<i; j++) {
                G[i][j] = G[j][i] = calc(i, j);
            }
            G[i][i] = 0;
        }
 
        printf("Scenario #%d\nFrog Distance = %.3f\n\n", ++cnt, dijkstra());
    }
 
    return 0;
}

2.Heavy Transportation(最小路径最大值)

题目链接

题目大意:N个点,M条边,每条边有权值。求一条1号点到N号点的路径,要求使得路径中的边权最小值最大。

思路:这道题和上一道思路类似,我们可以改变dis数组的含义,在更新dis的过程中与上一道题相反即可

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1010, M = 1e6+10;
int h[N], e[M], ne[M], w[M], idx;
int dis[N],vis[N];

int n,m;

void add(int a,int b,int c){
	e[idx] = b;
	ne[idx] = h[a];
	w[idx] = c;
	h[a] = idx++;
}

int dj(){
	for(int i=h[1];~i;i=ne[i]){
		int j = e[i];
		dis[j] = w[i];
	}

	for(int i=0;i<n;i++){
		int t = -1;
		for(int j=1;j<=n;j++){
			if(!vis[j]&&(t==-1||dis[t]<dis[j])){
				t = j;
			}
		}
		vis[t] = 1;
		for(int i = h[t]; ~i ; i = ne[i]){
			int j = e[i];
			int minn = min(w[i],dis[t]);
			dis[j] = max(dis[j],minn); 
		} 
	}
	return dis[n];
}

int main(){
	
	int t;
	scanf("%d",&t);
	int cnt=0;
	while(t--){
	
		scanf("%d%d",&n,&m);
		memset(h,-1,sizeof h);
		idx = 0;
		memset(vis,0,sizeof vis);
		memset(dis,-0x3f,sizeof dis);
		while(m--){
			int a ,b,c;
			scanf("%d%d%d",&a,&b,&c);
			add(a,b,c);
			add(b,a,c);
		}
		printf("Scenario #%d:\n%d\n\n",++cnt,dj());
	}
}

判断正环的题目

Currency Exchange

 思路:如果有正环,那么一定可以循环无限次,让初始值变正

#include<iostream>
#include<cstring>
#include<queue>

using namespace std;
const int N = 200, M = N * 2;

int h[N],e[M],ne[M],idx;
double wr[M],w[M];

int vis[N];
double dis[N];
int cnt[N];
int n,m,s;
double st;

void add(int a,int b,double r,double c){
	e[idx] = b;
	ne[idx] = h[a];
	wr[idx] = r;
	w[idx] = c;
	h[a] = idx++;
}

bool spfa(int s)
{
    queue<int > q;
	q.push(s);
	vis[s] = true;
	dis[s] = st;
    
	for (int i = 1; i <= n; i ++)
	{
		if(i==s)continue;
		q.push(i);
		vis[i] = 1;
	}
	
	
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        vis[t] = false;
        
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if ((dis[t] - w[i]) * wr[i] > dis[j])
            {
                dis[j] = (dis[t] - w[i]) * wr[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n) return true;
                if (!vis[j])
                {
                    q.push(j);
                    vis[j] = true;
                }
            }
        }
    }
    
    return false;
}


int main(){
	cin >> n >> m >> s >>st;
	memset(h,-1,sizeof h);
	while(m--){
		int a,b;
		double r1,r2,c1,c2;
		cin >> a >> b >> r1 >> c1 >> r2 >> c2;
		add(a,b,r1,c1);
		add(b,a,r2,c2);
	}
	
	if(spfa(s)){
		puts("YES");
	} 
	else{
		puts("NO");
	}
}

相关题目:https://vjudge.net/problem/POJ-3259

MPI Maelstrom

MPI Maelstrom - POJ 1502 - Virtual Judge

题目大意:

题目大意:给定一个下三角矩阵,询问从1开始到其他点的最短路径中,最长的那个是多少

思路:其实就是一个很简单的dijkstra的板子题,我们可以一遍dijkstra,跑一遍最短路,找到最大的值输出即可

代码复制的别人的

#include <iostream>
#include <string.h>
#include <cstdio>
using namespace std;
const int N=110;
#define MAX 0x7fffffff
int map[N][N];
int dis[N],visted[N],n;
int dijkstra(){
	memset(visted,0,sizeof(visted));
	for(int i=0;i<=n;++i){
      dis[i]=MAX;	   
	}
	dis[1]=0;
	int pos=1;visted[pos]=1;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
		  if(!visted[j]&&map[pos][j]!=MAX&&dis[j]>dis[pos]+map[pos][j])
			  dis[j]=dis[pos]+map[pos][j];
		}
		int mmin=MAX;
		for(int j=1;j<=n;++j){
			if(!visted[j]&&dis[j]<mmin){
			  mmin=dis[j];
			  pos=j;
			}
		}
		//printf("pos==%d\n",pos);
		visted[pos]=1;
	}
	int mmax=0;
	for(int i=1;i<=n;++i){
	  //printf("%d  ",dis[i]);
	  if(mmax<dis[i])
		  mmax=dis[i];
	}
	//cout<<endl;
	return mmax;
}
int main(){
  //freopen("1.txt","r",stdin);
  while(~scanf("%d",&n)){
	  int m;
	  char x;
	  for(int i=2;i<=n;++i){
		  for(int j=1;j<i;++j){
		    if(scanf("%d",&m)!=0)
				map[i][j]=map[j][i]=m;
			else{
			  map[i][j]=map[j][i]=MAX;
			  scanf("%c",&x);
			}
		  }
	  }
	  for(int i=1;i<=n;++i)
		  map[i][i]=0;
	  int xx=dijkstra();
	  printf("%d\n",xx);
  }
  return 0;
}

Cow Contest(floyd + 传递闭包)

Cow Contest - POJ 3660 - Virtual Judge

题目大意:有N头牛,评以N个等级,各不相同,先给出部分牛的等级的高低关系,问最多能确定多少头牛的等级

思路:如果有一头关系已经确定, 那么他一定与其他所有牛关系已经确定了,即n-1头牛

#include<iostream>
#include<cstring>
using namespace std;
const int N = 110, M = N * 2;

int g[N][N];

int n,m;

void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				g[i][j]|=g[i][k]&&g[k][j];
			}
		}
	}
}

int main(){
	cin >> n >> m;
	while(m--){
		int a, b;
		cin >> a >> b;
		g[a][b] = 1; 
	}
	
	floyd();
	
	int res = 0;
	for(int i=1;i<=n;i++){
		int cnt = 0;
		for(int j=1;j<=n;j++){
			if(g[i][j]||g[j][i]) cnt++;
		}
		if(cnt==n-1)res++;
	}
	
	cout << res <<endl;
}

Arbitrage

套利是指利用货币汇率的差异,将一种货币的一个单位转换为同一货币的多个单位。例如,假设1美元买0.5英镑,1英镑买10.0法国法郎,1法国法郎买0.21美元。然后,通过兑换货币,聪明的交易者可以从1美元开始,购买0.5 * 10.0 * 0.21 = 1.05美元,获得5%的利润。
您的工作是编写一个程序,以货币汇率列表作为输入,然后确定是否可能进行套利。

输入:

将包含一个或多个测试用例。每个测试用例的第一行有一个整数n (1<=n<=30),表示不同货币的数量。接下来的n行每一行都包含一种货币的名称。名称中不会出现空格。下一行包含一个整数m,表示接下来的表的长度。最后的m行分别包含源货币的名称ci、表示从ci到cj的汇率的实数rij和目标货币的名称cj。没有出现在表格中的交易是不可能的。

测试用例通过空行彼此分开。对于n,输入以0(0)的值结束

思路:和上面找正环的思路差不多

//floyed
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
 
using namespace std;
int n,m;
double dist[40][40];
map<string,int> money;
 
void init()
{
    for(int i = 0;i < n;i++) {
        for(int j = 0;j < n;j++){
            if(i == j) dist[i][j] = 1;
            else dist[i][j] = 0;
        }
    }
}
 
void floyd()
{
    for(int k = 0;k < n;k++) {
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < n;j++) {
                if(dist[i][j] < dist[i][k] * dist[k][j])
                    dist[i][j] = dist[i][k] * dist[k][j];
            }
        }
    }
}
 
int main()
{
    int cas = 1;
    string s,ss;
    double r;
    while(~scanf("%d",&n) &&n) {
        init();
        for(int i = 0;i < n;i++) {
            cin >> s;
            money[s] = i;
        }
        cin >> m;
 
        for(int i = 0;i < m;i++) {
            cin >> s >> r >> ss;
            dist[money[s] ][money[ss] ] = r;
        }
        floyd();
        printf("Case %d: ",cas++);
        for(int i = 0;i < n;i++) {
            if(dist[i][i] > 1) {
                cout << "Yes" << endl;
                break;
            }
            else if(i == n - 1)
                cout << "No" <<endl;
        }
    }
    return 0;
}

差分约束见另外一篇文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值