当前位置: 首页 > 工具软件 > moyu > 使用案例 >

Day 7 A - Age of Moyu HDU 6386 | 最短路 | SPFA | 链式前向星

宋英杰
2023-12-01
  • 松弛:原来用一根橡皮筋连接p和w两点,现在有一点v到w的路径更短,现在把橡皮筋w点的另一端p换成v点,这样缓解橡皮筋紧绷的压力,让其变得松弛。
    来自:原博
  • 关于SPFA算法
    来自:原博
  • 链式前向星
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f//可以使用memset

//链式前向星+SPFA

struct edge{
    int next;//下一条边的存储下标(默认0
	int to;//这条边终点
	int lenth;//权值

}a[400020];//结构体数组edge存边,edge[i]表示第i条边

int head[200020];// head[i]存以i为起点的最后一条边的编号
int n,m,q, nn;
int vis[200020];

struct nodope{
	int val;		//val花费    d结束时的颜色
	set<int> d;
}d[200020];		//当前点

int spfa()
{
	memset(vis,0,sizeof vis);
	vis[1] = 1;
	queue<int> q;
	q.push(1);


	for(int i=0;i<=n;i++)
    {
        d[i].val=INF,
        d[i].d.clear();
    }

	d[1].val=0;

	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i!=-1;i=a[i].next)//遍历以u为起点的点
		{
			int to = a[i].to;
			if(d[to].val>(d[u].val+(d[u].d.find(a[i].lenth)==d[u].d.end())))
			{
				d[to].val = d[u].val+(d[u].d.find(a[i].lenth)==d[u].d.end());
				d[to].d.clear();
				d[to].d.insert(a[i].lenth);
				if(!vis[to])
                {
                    q.push(to);
                    vis[to] = 1;
                }
			}
			else if(d[to].val==(d[u].val+(d[u].d.find(a[i].lenth)==d[u].d.end()))&&d[to].d.find(a[i].lenth)==d[to].d.end())
			{
				d[to].d.insert(a[i].lenth);
				if(!vis[to])
                {
                    q.push(to);
                    vis[to] = 1;
                }
			}
		}
	}
	if(d[n].val != INF)
        printf("%d\n",d[n].val);
	else
        printf("-1\n");
	return 0;
}

void add(int x,int y,int z)
{
    //nn即cnt 为边的计数 从0开始计
	a[nn].lenth=z;//终点
	a[nn].to=y;//权值
	a[nn].next=head[x];//以x为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号
	head[x]=nn++;//更新以x为起点的上一条边的编号
	//若以点i为起点的边新增了一条,在edge中的下标为j.
	//那么edge[j].next=head[i];然后head[i]=j.
	//即每次新加的边作为第一条边,最后倒序遍历


	/*题设是双向边*/
	a[nn].lenth=z;
	a[nn].to=x;
	a[nn].next=head[y];
	head[y]=nn++;
}

int main()
{
	//freopen("2.txt","r",stdin);
	while(~scanf("%d%d",&n,&m))
	{
	    for(int i = 0;i <= n;i++)
            head[i] = -1;//head初始化-1

		nn = 0;//cnt

		for(int i = 0;i < m;i++)
        {
            int x, y, z;
            scanf("%d %d %d", &x, &y, &z);
            add(x, y, z);
        }

		spfa();
	}
	return 0;
}

SPFA 模板来自上?原博

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;

const int M=10005;

struct A{
	int y,time,next;
}a[M<<1];

int pre[M],cent=0;//链式前向星数组
int vis[M],ven[M],nums[M];

//SPFS数组,vis记录最短路,ven记录是否在队列,nums记录入队次数

void add(int x,int y,int k)//链式前向星,加入节点
{
	a[cent].y=y, a[cent].time=k, a[cent].next=pre[x];
	pre[x]=cent++;
}

bool SPFA(int s,int n)
{
	queue <int> q;
	memset(vis,inf,sizeof(vis));
	memset(ven,0,sizeof(ven));
	memset(nums,0,sizeof(nums));
	vis[s]=0;//初始化距离
	ven[s]=1,nums[s]++;//标记s节点在队列,队列次数+1
	q.push(s);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();//出队
		ven[x]=0;//标记不在队列
		for(int i=pre[x]; ~i; i=a[i].next)//遍历与x节点连通的点
		{
			int y=a[i].y;
			if(vis[y]>vis[x]+a[i].time)//更新
			{
				vis[y]=vis[x]+a[i].time;
				if(!ven[y])
				//由于更新了节点,所以后续以这个为基础的最短路,也要更新下
				//所以如果在队列就不用加入,不在的话加入更新后续节点
				{
					q.push(y);
					ven[y]=1;//标记这个节点在队列中
					nums[y]++;//记录加入次数
					if(nums[y]>n)//如果这个点加入超过n次,说明存在负圈,直接返回
						return false;
				}
			}
		}
	}
	return true;
}

int main()
{
	int n,m,t;
	int b[M],c[M];
	while(cin>>n>>m>>t)
	{
		cent=0;
		memset(pre,-1,sizeof(pre));
		for(int i=0;i<n;i++)
		{
			int x,y,k;
			cin>>x>>y>>k;
			add(x,y,k);
			add(y,x,k);
		}
		for(int i=0;i<m;i++)
		{
			cin>>b[i];
		}
		for(int i=0;i<t;i++)
		{
			cin>>c[i];
		}
		int minn=inf;
		for(int i=0;i<m;i++)//遍历多个找寻最小
		{
			SPFA(b[i],n);
			for(int j=0;j<t;j++)
				minn=min(minn,vis[c[j]]);
		}
		cout<<minn<<endl;
	}
 }
 类似资料: