zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    hdu 1108 最小公倍数
    hdu 1106 排序
    hdu 1097 A hard puzzle
    hdu 1076 An Easy Task
    hdu 1064 Financial Management
    hdu 1061 Rightmost Digit
    hdu 1050 Moving Tables
    hdu 1060 Leftmost Digit
    hdu 1049 Climbing Worm
    hdu1104
  • 原文地址:https://www.cnblogs.com/BeautifulWater/p/15020423.html
Copyright © 2011-2022 走看看