zoukankan      html  css  js  c++  java
  • spfa学习笔记

    spfa它死了

    ——by 大佬

    但是本蒟蒻还是一如既往的使用spfa...
    因为太弱了,其他什么都不会。于是就疯狂开O2跪倒在spfa上。

    例题——汽车加油行驶问题

    loj跳转链接

    luogu跳转链接

    题目描述

    给定一个 N×N 的方形网格,设其左上角为起点◎,坐标为 (1,1)(1,1) ,X 轴向右为正, Y 轴向下为正,每个方格边长为 1 ,如图所示。

    233

    一辆汽车从起点◎出发驶向右下角终点▲,其坐标为 (N,N) 。

    在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:

    • 汽车只能沿网格边行驶,装满油后能行驶 K 条网格边。出发时汽车已装满油,在起 点与终点处不设油库。
    • 汽车经过一条网格边时,若其 X 坐标或 Y 坐标减小,则应付费用 ext{B}B ,否则免付费用。
    • 汽车在行驶过程中遇油库则应加满油并付加油费用 A。
    • 在需要时可在网格点处增设油库,并付增设油库费用 C (不含加油费用 ext{A}A )。
    • N , K , A , B , C 均为正整数, 且满足约束: 102≤N≤100,2≤K≤10。

    设计一个算法,求出汽车从起点出发到达终点的一条所付费用最少的行驶路线。

    输入格式

    文件的第一行是 N,K,A,B,C 的值。

    第二行起是一个 N∗N 的 0-1 方阵,每行 N 个值,至 N+1 行结束。

    方阵的第 i 行第 j 列处的值为 1 表示在网格交叉点 (i,j) 处设置了一个油库,为 0 时表示未设油库。各行相邻两个数以空格分隔。

    输出格式

    程序运行结束时,输出最小费用。

    样例输入

    9 3 2 3 6
    0 0 0 0 1 0 0 0 0
    0 0 0 1 0 1 1 0 0
    1 0 1 0 0 0 0 1 0
    0 0 0 0 0 1 0 0 1
    1 0 0 1 0 0 1 0 0
    0 1 0 0 0 0 0 1 0
    0 0 0 0 1 0 0 0 1
    1 0 0 1 0 0 0 1 0
    0 1 0 0 0 0 0 0 0
    

    样例输出

    12
    

    思路

    当然食用spfa啦。

    但本蒟蒻不会分层。所以就二维spfa啦。

    基本思路就是:一开始先把(1,1)点的状态扔进队列。

    然后分类讨论

    没了。。

    丑陋无比的代码:

    #include<algorithm>
    #include<bitset>
    #include<complex>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<iterator>
    #include<limits>
    #include<list>
    #include<locale>
    #include<map>
    #include<memory>
    #include<new>
    #include<numeric>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<typeinfo>
    #include<utility>
    #include<valarray>
    #include<vector>
    #include<cctype>
    #include<cerrno>
    #include<cfloat>
    #include<ciso646>
    #include<climits>
    #include<clocale>
    #include<cmath>
    #include<csetjmp>
    #include<csignal>
    #include<cstdarg>
    #include<cstddef>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #define ll long long
    #define mod 100003
    #define E 1000010
    using namespace std;//丑陋的头文件终于结束
    inline ll read(){
    	ll res=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    	return res*f;
    }//丑陋的读优
    inline void write(ll x){
    	if(x<0) putchar('-'),x=-x;
    	if(x<10) putchar(x+'0');
    	else{
    		write(x/10);
    		putchar(x%10+'0');
    	}
    }//丑陋的输优
    //queue<ll> q;
    //set<ll> s;
    //priority_queue<ll> q1;
    //priority_queue<ll,vector<ll>,greater<ll> > q2;
    //list<ll> l;
    //stack<ll> s;
    int n,k,a,b,c;
    int has[110][110];//有木有油箱
    int dis[110][110][15];//当前状态的最小花费
    struct node{
    	int x,y,z;//状态记录,分别为x坐标、y坐标、还有多少油
    };
    struct queue{
    	node val[10000000];int s,t;
    	void clean(){
    		memset(val,0,sizeof(val));
    		s=0;t=0;//清空(貌似没有用到过)
    	}
    	void push(node tmp){
    		++t;
    		t%=10000000;
    		val[t]=tmp;//加入队尾
    	}
    	inline node front(){
    		return val[(s+1)%10000000];//循环队列,get队首
    	}
    	void pop(){
    		s++;//弹出
    		s%=10000000;//循环队列
    	}
    	bool empty(){//判断是否为空
    		if(s>=t) return 1;
    		else return 0;
    	}
    }q;//队列
    node make(int x,int y,int z){
    	node pp;pp.x=x;pp.y=y;pp.z=z;
    	return pp;
    }//方便快捷一些
    const int dx[]={0,0,1,-1},
              dy[]={1,-1,0,0};//四个方向
    void print(){
    	system("cls");
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++){
    			int ss=2e9;
    			for(int l=0;l<=k;l++) ss=min(ss,dis[i][j][l]);
    			cout<<ss<<" ";
    		}
    		cout<<endl;
    	}
    }//调试输出函数
    void spfa(){//spfa开始
    	while(!q.empty()){
    		node u=q.front();q.pop();
    		int x=u.x,y=u.y,K=u.z;//取出队首元素
    		if(K==0){//没油了
    			if(has[x][y]==1){//真棒!这里就有油箱
    				if(dis[x][y][K]+a<dis[x][y][k]){
    					dis[x][y][k]=min(dis[x][y][k],dis[x][y][K]+a);//加油花费a
    					q.push(make(x,y,k));
    				}
    			}else{//这里没有油箱
    				if(dis[x][y][k]>dis[x][y][K]+c+a){
    					dis[x][y][k]=min(dis[x][y][k],dis[x][y][K]+c+a);//新建油箱花费c,加油花费a
    					q.push(make(x,y,k));
    				}
    			}
    		}else{
    			if(has[x][y]==1){//坑,如果经过油箱必须加油
    				if(dis[x][y][K]+a<dis[x][y][k]){
    					dis[x][y][k]=dis[x][y][K]+a;//愉快的花费a
    					q.push(make(x,y,k));continue ;//因为加油,本次状态取消,直接退出
    				}
    			}
    			for(int i=0;i<4;i++){
    				int xx=x+dx[i],yy=y+dy[i];//四个方向
    				if(xx>=1&&xx<=n&&yy>=1&&yy<=n){//判断是否越界
    					if(dis[xx][yy][K-1]>dis[x][y][K]+((i==1||i==3)?b:0)){//如果往负方向花费b
    						dis[xx][yy][K-1]=dis[x][y][K]+((i==1||i==3)?b:0);//更新
    						q.push(make(xx,yy,K-1));//保存状态
    					}
    				}
    			}
    			
    		}
    //		print();
    	}
    }
    int main(){
    	n=read();k=read();a=read();b=read();c=read();
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++){
    			has[i][j]=read();
    		}
    	}//丑陋无比的读入
    	memset(dis,63,sizeof(dis));//初始化
    	dis[1][1][k]=0;//初始化
    	q.push(make(1,1,k));//塞入队列
    	spfa();//spfa
    	int ans=2e9;//取最小值前设定最大
    	for(int i=0;i<=k;i++) ans=min(ans,dis[n][n][i]);//更新,因为终点有很多状态,取最小值
    	cout<<ans<<endl;//输出
        return 0;
    }
    
  • 相关阅读:
    java中几个常见的Arrays下的类
    java二分查找法的实现过程
    java冒泡排序的实现过程
    java数组中选择排序的实现
    正则表达式
    StringBuilder的使用与总结
    String的几个常用API
    关于API,注释文档以及String
    关于sqlserve2000和sqlserver2005以后版本配置连接池的一些思路
    专题-X教育
  • 原文地址:https://www.cnblogs.com/yzx1798106406/p/10323499.html
Copyright © 2011-2022 走看看