Title

SPFA BFS(30%)

spfa

原理

  • 确定一个起点,如果满足条件(能够使得路径缩小时),改变路径长度,并将处在起点周围且仍未纳入到队列中的点纳入到队列中,然后依次对队列中的点做相同的操作,直至队列为空。

前置知识

邻接表建图

  • idx表示的是当前按顺序已经记录到第n条边。
  • e[idx]数组用来存放当前有向线段指向的下一端点。
  • ne[idx]表示的是同属某一起点的指向的下一条边的idx位置
  • h[aa]表示的是以aa为起点的最近新添加的起点。
  • w数组用来存放边权。

1.单向建图

const int N = 2e3+10,M =2e5+10;
int idx=0,e[M],ne[M],h[M];
double w[M];
void add(int aa,int bb,double cc)
{
	e[idx]=bb,ne[idx]=h[aa],w[idx]=cc,h[aa]=idx++;
}

2.双向建图

  • 当题目中有提示双向道路
const int N = 2e3+10,M =2e5+10;
int idx=0,e[M],ne[M],h[M];
double w[M];
void add(int aa,int bb,double cc)
{
	e[idx]=bb,ne[idx]=h[aa],w[idx]=cc,h[aa]=idx++;
}

注意:记得在main函数里初始化h数组:memset(h,-1,sizeof(h));

~i

  • 循环中i==-1的简化写法(ne数组各个元素的尽头是-1,相当于是再也找不到下一条边了)

沿着某一点转圈,遍历所有相邻边

 for(int i=h[t];~i;i=ne[i])
 {
 
 }

0x3f

  • memset0x3f填充数组的原因是0x3f可以用来表示”无穷大“,且不是无穷大中的天花板(也就是说可以接受多个0x3f3f3f3f相加且不爆数据范围的情况)。

spfa()特色

  • dist数组

习题

裸题

1129. 热浪

#include<bits/stdc++.h>
using namespace std;
const int N =3000,M=20000;

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

bool vis[N];
int spfa(int st,int ed)
{
	int dist[M];
	memset(vis,false,sizeof(vis));
	memset(dist,0x3f,sizeof(dist));
	queue<int > q;
	q.push(st);
	dist[st]=0;
	vis[st]=true;
	while(q.size())
	{
		int t = q.front();
		q.pop();
		vis[t]=false;
		for(int i=h[t];~i;i=ne[i])
		{
			//cout<<1<<endl;
			int j=e[i];
			if(dist[j]>dist[t]+w[i])
			{
				dist[j]=dist[t]+w[i];
				if(!vis[j])
				{
					vis[j]=true;
					q.push(j);
				}
			}
		}
	}
	
	return dist[ed];
}
int main()
{
	int n,m,st,ed;
	cin>>n>>m>>st>>ed;
	memset(h,-1,sizeof(h));
	for(int i = 0;i<m;i++)
	{
		int a,b,w;
		cin>>a>>b>>w;
		add(a,b,w),add(b,a,w);
	
	}	cout<<spfa(st,ed);
	return 0;
}

多源最短路径

  • 把每个点都扔进spfa里面,并在搜寻到的结果中找到最小值

1127. 香甜的黄油

农夫John发现了做出全威斯康辛州最甜的黄油的方法:糖。

把糖放在一片牧场上,他知道 N 只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。

当然,他将付出额外的费用在奶牛上。

农夫John很狡猾,就像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。

他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。

农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。

给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。

数据保证至少存在一个牧场和所有牛所在的牧场连通

#include<bits/stdc++.h>
using namespace std;
const int N = 3e3;
typedef long long ll;
int n,p,c;
vector<int > cow_pos;//奶牛位置 

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

int spfa(int st)
{
	int dist[N];
	bool vis[N];
	memset(dist,0x3f,sizeof(dist));
	memset(vis,false,sizeof(vis));
	
	queue<int > q;
	q.push(st);
	dist[st]=0;
	vis[st]=true;
	while(q.size())
	{
	    int t=q.front();
	    q.pop();
	    vis[t]=false;
	    for(int i=h[t];~i;i=ne[i])
	    {
	    	int j=e[i];
	    	if(dist[j]>dist[t]+w[i])
	    	{
	    		dist[j]=dist[t]+w[i];//cout<<"jiancha   "<<j<<"   "<<dist[j]<<endl;
	    		if(!vis[j])
	    		{
	    			vis[j]=true;
	    			q.push(j);
				}
			}
		}
	}
	ll res=0;
	//for(int i=1;i<=p;i++)//只要接回所有奶牛,而不是到所有牧场的距离和
	for(int i=0;i<cow_pos.size();i++)
	{
		//cout<<cow_pos[i]<<endl;
	    res+=dist[cow_pos[i]];
	//	cout<<i<<"  "<<res<<endl;
	}
        
    return res;
}

int main()
{
	cin>>n>>p>>c;
	memset(h,-1,sizeof(h));
	for(int i=0;i<n;i++)
    {
    	int x;
    	cin>>x;
    	cow_pos.push_back(x);
	}
	for(int i=0;i<c;i++)
	{
		int aa,bb,cc;
		cin>>aa>>bb>>cc;
		add(aa,bb,cc);add(bb,aa,cc);
	}
	int ans=0x3f3f3f3f;
	for(int i=1;i<=p;i++)
	   ans = min(ans,spfa(i));
	cout<<ans;
	return 0;
}

dist数组的变形:乘法dist

1128最小花费

在 n个人中,某些人的银行账号之间可以互相转账。

这些人之间转账的手续费各不相同。

给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 A最少需要多少钱使得转账后 BB 收到 100 元。

  • 注意double初始化0x3f = 0.0004767
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int n,m;

const int N = 2e3+10,M =2e5+10;
int idx=0,e[M],ne[M],h[M];
double w[M];
void add(int aa,int bb,double cc)
{
	e[idx]=bb,ne[idx]=h[aa],w[idx]=cc,h[aa]=idx++;
}

double spfa(int st,int ed)
{
	double dist[M];
	bool used[N];
	memset(dist,0x7f,sizeof(dist));//注意double初始化0x3f = 0.0004767
	memset(used,false,sizeof(used));
	
	queue<int > q;
	q.push(st);
	used[st]=true;
	dist[st]=1;
	while(q.size())
	{
		int t=q.front();
		q.pop();
		used[t]=false;
		
		for(int i=h[t];~i;i=ne[i])
		{
			int j=e[i];
			if(dist[j]<dist[t]*(1-w[i]))
			{
				dist[j]=dist[t]*(1-w[i]);
				if(!used[j])
				{
					q.push(j);
					used[j]=true;
				}
			}
		}
	}
	return dist[ed];
}

int main()
{
	memset(h,-1,sizeof(h));
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int x,y, z;
		cin>>x>>y>>z;
		add(x,y,1.0*z/100);add(y,x,1.0*z/100);
	}
	int st,ed;
	cin>>st>>ed;
	cout<<fixed<<setprecision(8)<<100/spfa(st,ed);
	return 0;
}
posted @ 2021-07-16 15:40  BeautifulWater  阅读(65)  评论(0编辑  收藏  举报