P4009 汽车加油行驶问题


本来想练一手费用流的 但是一看这个题分层图跑的更快 而且也是个很好的分层图板子

将图分为k+1层 因为充满油可以跑k条边 算上 开始的1 就是k+1层

首先 第x层图中的点[i,j] 连向第x+1层的点[i,j]的 边权为 0

如果该点不是加油站 第x层[i,j] 连向第x+1层[i,j+1] [i+1,j]边权为0

                        连向第x+1层[i-1,j] [i,j-1]边权为b

如果设该点为加油站 第x层[i,j] 连向 第1层 [i,j]边权为p+c

如果该点为加油站 第x层[i,j] 连向 第1层 [i,j] 边权为p

             第x层[i,j] 连向 第2层 [i,j+1] [i+1,j] 边权为0
                       
                        连向 第2层 [i-1,j] [i,j-1] 边权为b  

注意题目要求遇到油库就要加满油

#include
#include
#include
using namespace std;

const int INF=2e9;

int n,k,p,b,c,np;
int h[350005],ln[350005],q[900005];
bool vis[350005],oil;
struct rpg{
	int li,nx,ln;
}a[900005];

void add(int x1,int y1,int z1,int x2,int y2,int z2,int ln){
	int ls=n*n*(z1-1)+(x1-1)*n+y1,nx=n*n*(z2-1)+(x2-1)*n+y2;
	a[++np]=(rpg){h[ls],nx,ln};
	h[ls]=np;
}

void spfa(){
	for(int i=1;i<=n*n*(k+1);++i) ln[i]=INF;
	int hd=1,tl=1;
	q[hd]=1;
	ln[1]=0;
	while(hd<=tl){
		int nw=q[hd++];
		vis[nw]=0;
		for(int i=h[nw];i;i=a[i].li){
			if(ln[a[i].nx]>ln[nw]+a[i].ln){
				ln[a[i].nx]=ln[nw]+a[i].ln;
				if(!vis[a[i].nx]){
					vis[a[i].nx]=1;
					q[++tl]=a[i].nx;
				}
			}
		}
	}
}

int main(){
	scanf("%d%d%d%d%d",&n,&k,&p,&b,&c);
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			scanf("%d",&oil);
			for(int l=1;l<=k;++l){
				add(i,j,l,i,j,l+1,0);
			}
			if(oil){
				for(int l=2;l<=k+1;++l){
					add(i,j,l,i,j,1,p);
				}
				if(i1) add(i,j,1,i-1,j,2,b);
				if(j>1) add(i,j,1,i,j-1,2,b);
			}else{
				for(int l=1;l<=k;++l){
					if(i1) add(i,j,l,i-1,j,l+1,b);
					if(j>1) add(i,j,l,i,j-1,l+1,b);
				}for(int l=2;l<=k+1;++l){
					add(i,j,l,i,j,1,p+c);
				}
			}
		}
	}spfa();
	int ans=INF;
	for(int i=1;i<=k+1;++i) ans=min(ans,ln[n*n*i]);
	printf("%d\n",ans);
	return 0;
}