[kuangbin带你飞]专题四 最短路练习 A-E

本文解析了五道涉及图论算法的经典编程题目,包括SPFA、Dijkstra、Floyd等算法的应用,并针对每道题目的特点进行了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:https://vjudge.net/contest/66569#overview

A:

#include <iostream>   
#include <queue>  
#include <cstdio> 
using namespace std;  
const int maxn=1005;  
vector <pair<int,int> >E[maxn];   
int dis[maxn];   
bool vis[maxn];   
void Init()  
{  
    for(int i=0;i<maxn;i++)    
    E[i].clear(),vis[i]=false,dis[i]=1e9;  
}  
void spfa(int s,int t)
{  
    queue<int>q;  
    q.push(s);  
    dis[s]=0;  
    vis[s]=true;   
    while(!q.empty())  
    {  
        int now=q.front();  
        q.pop();  
        vis[now]=false;   
        for(int i=0;i<E[now].size();i++)   
        {  
            int next=E[now][i].first;  
            if(dis[next]>dis[now]+E[now][i].second)  
            {  
                dis[next]=dis[now]+E[now][i].second;  
                if(vis[next])continue; 
                vis[next]=true;  
                q.push(next);   
            }   
        }  
    }  
}
int main()
{
	int T,N;
	while(scanf("%d%d",&T,&N)==2)
	{
		Init();
		for(int i=1;i<=T;i++)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			E[x].push_back(make_pair(y,z));
			E[y].push_back(make_pair(x,z));
		}
		spfa(N,1);
		printf("%d\n",dis[1]);
	}
	return 0;	
} 

B:最短路的变形,求最小边.数据小,可以写个floyd交.

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
double map[205][205];
int x[205];
int y[205];
double  dis(int i,int j)
{
	return sqrt(double(x[i]-x[j])*(x[i]-x[j])+double(y[i]-y[j])*(y[i]-y[j]));
}
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++)
		    map[i][j]=min(map[i][j],max(map[i][k],map[k][j]));
}
int main()
{
   int n,q=1; 
    while(scanf("%d",&n)==1&&n)  
    { 
        memset(map,0x3f,sizeof(map));  
        for(int i=1; i<=n; i++)  
            scanf("%d%d",&x[i],&y[i]);  
        for(int i=1; i<=n; i++)  
            for(int j=i+1; j<=n; j++)  
                map[i][j]=map[j][i]=dis(i,j);  
        floyd(n);  
        printf("Scenario #%d\nFrog Distance = %.3f\n\n",q++,map[1][2]);  
    }  
}

C:就是B题的反过来,求最大的边。但是floyd过不了,用了我的dij模版改了下条件。dis[next]=max(dis[next],min(dis[now],cost[now][next])

#include <iostream>   
#include <queue>  
#include <cstdio>
#include <vector>
#include <algorithm> 
using namespace std;  
const int maxn=1005; //顶点数自己定   
vector <pair<int,int> >E[maxn];//存图   
int dis[maxn];   
void Init()  
{  
    for(int i=0;i<maxn;i++)    
    E[i].clear(),dis[i]=0;  
}  
void Dijkstra(int s,int t)//源点,终点.   
{  
    priority_queue<pair<int,int> >q;  
        dis[1]=1e9;  
        q.push(make_pair(dis[1],1));  
        while(!q.empty())  
        {  
            int now=q.top().second;  
            q.pop();  
            for(int i=0;i<E[now].size();i++)  
            {  
                int next=E[now][i].first;  
                if(dis[next]<min(dis[now],E[now][i].second))  
                {  
                    dis[next]=min(dis[now],E[now][i].second);  
                    q.push(make_pair(dis[next],next));  
                }  
            }   
        }  
}  
  
int main()  
{  
	int t; 
    int n,m;
    int cas=1;
	scanf("%d",&t);  
  	while(t--)
  	{
  		scanf("%d%d",&n,&m); 	
        Init();  
        for(int i=0;i<m;i++)  
        {  
            int x,y,z;  
            scanf("%d%d%d",&x,&y,&z); 
            E[x].push_back(make_pair(y,z));  
            E[y].push_back(make_pair(x,z));  
        }  
        Dijkstra(1,n);   
        printf("Scenario #%d:\n",cas++);  
       printf("%d\n",dis[n]);
       if(t!=0)
       printf("\n");
      
  	}
    return 0;  
}  

D:n个源点,1个x目标点.单源找来回最大的最短路. 回来只要找一次,出发每个点找一次.

#include <cstdio>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1005;
vector<pair<int,int> >E[maxn];
int dis[maxn]; 
int go[maxn];
void dij(int s)
{
	for(int i=0;i<maxn;i++)
	dis[i]=1e9;
	dis[s]=0;
	priority_queue<pair<int,int> >q;
	q.push(make_pair(-dis[s],s));
	while(!q.empty())
	{
		int now=q.top().second;
		q.pop();
		for(int i=0;i<E[now].size();i++)
		{
			int next=E[now][i].first;
			if(dis[next]>dis[now]+E[now][i].second)
			{
				dis[next]=dis[now]+E[now][i].second;
				q.push(make_pair(-dis[next],next));
			}
		} 
	}
}
int main()
{
	int n,m,x;
	scanf("%d%d%d",&n,&m,&x);
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		E[x].push_back(make_pair(y,z));
	}
	for(int i=1;i<=n;i++)
	{
		if(n==x)continue;
		dij(i),go[i]=dis[x];
	}
	dij(x);
	int ans=-1;
	for(int i=1;i<=n;i++)
		ans=max(ans,go[i]+dis[i]);
	printf("%d\n",ans);
}

E:读这种长的英文是真烦,

第一行叫你输4个数 N,M,S,V . N种货币种类(用数字代替) M个城市(每个城市有两种货币可以进行兑换) S(你的钱的种类) V(你的钱数)

接下来M行自然就是6个数代表该城市的信息:货币A,货币B,A->B的汇率,A->B的佣金,B->A的汇率,B->A的佣金.(题目有给兑换公式)

问你有没办法通过一系列货币的转换,最后当货币换回原来货币种类的时候,V变多了。 事实上就是判断正环的问题。那就改下判断负环的一些条件就好了。改下初始值

初始d(S)=V   而源点到其他店的距离(权值)初始化为无穷小(0),当s到其他某点的距离能不断变大时,说明存在最大路径,用BF判断是第n次循环还变大就有正环,spfa就是入队超过n次说明有正环,这里我用spfa做。。

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstdio>
using namespace std;
const int maxn=105;
int n,m,s;
double v;
struct exc{
	double r,c;
}; 
vector<pair<int,exc> >E[maxn];
double dis[maxn];
bool vis[maxn];
int time[maxn];
bool spfa()
{
	for(int i=0;i<maxn;i++) vis[i]=false;
	for(int i=0;i<maxn;i++)	dis[i] = 0;
	for(int i=0;i<maxn;i++)	time[i]= 0;
	dis[s]=v;
	vis[s]=true;
	queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		time[now]++;
		vis[now]=false;
		if(time[now]>n) return true;
		for(int i=0;i<E[now].size();i++)
		{
			int next=E[now][i].first;
			double temp=0;
			temp=(dis[now]-E[now][i].second.c)*(E[now][i].second.r);
			if(dis[next]<temp)
			{
				dis[next]=temp;
				if(vis[next])continue;
				vis[next]=true;
				q.push(next);
			}
		} 
	} 
        return false;  
}


int main()
{
	scanf("%d%d%d%lf",&n,&m,&s,&v);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		double xyr,xyc,yxr,yxc;
		scanf("%d%d%lf%lf%lf%lf",&x,&y,&xyr,&xyc,&yxr,&yxc);
		exc temp;
		temp.r=xyr,temp.c=xyc;
		E[x].push_back(make_pair(y,temp));
		temp.r=yxr,temp.c=yxc;
		E[y].push_back(make_pair(x,temp));
	}
	printf("%s",spfa()?"YES":"NO");
}

剩下有空补,先去补其他知识.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值