【线性规划与网络流24题】汽车加油行驶问题 分层图

汽车从起点到终点的行驶问题,需经过一个N*N的网格,遵循特定规则,包括支付网格费用B和加油费用A。目标是找到最低费用路径。输入包括油库位置和费用参数,输出是最小费用。该问题可以通过网络流算法解决,考虑不同情况下的费用转移和油库使用。
摘要由CSDN通过智能技术生成

汽车加油行驶问题

Time Limit: 1 Sec   Memory Limit: 128 MB

Description

给定一个 N*N的方形网格,设其左上角为起点◎,坐标为( 1,1),X轴向右为正, Y轴向下为正,每个方格边长为 1,如图所示。一辆汽车从起点◎出发驶向右下角终点▲,其坐标为( N,N)。在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则: 
(1)汽车只能沿网格边行驶,装满油后能行驶 K条网格边。出发时汽车已装满油,在起点与终点处不设油库。 
(2)汽车经过一条网格边时,若其 X坐标或 Y坐标减小,则应付费用 B,否则免付费用。 
(3)汽车在行驶过程中遇油库则应加满油并付加油费用 A。 
(4)在需要时可在网格点处增设油库,并付增设油库费用 C(不含加油费用 A)。 
(5)(1)~(4)中的各数 N、K、A、B、C均为正整数,且满足约束:2 ≤ N ≤ 100,2 ≤ K ≤ 10。设计一个算法,求出汽车从起点出发到达终点的一条所付费用最少的行驶路线。 

对于给定的交通网格,计算汽车从起点出发到达终点的一条所付费用最少的行驶路线。 

Input

文件的第一行是 N,K,A,B,C的值。第二行起是一个 N*N的 0-1方阵,每行 N个值,至 N+1行结束。方阵的第 i行第 j列处的值为 1表示在网格交叉点( i,j)处设置了一个油库,为 0时表示未设油库。各行相邻两个数以空格分隔。 

Output

输出最小费用

Sample Input

9 3 2 3 60 0 0 0 1 0 0 0 00 0 0 1 0 1 1 0 01 0 1 0 0 0 0 1 00 0 0 0 0 1 0 0 11 0 0 1 0 0 1 0 00 1 0 0 0 0 0 1 00 0 0 0 1 0 0 0 11 0 0 1 0 0 0 1 00 1 0 0 0 0 0 0 0

Sample Output

12

Source


    题意依然不加赘述,题解也不难说,就是比裸最短路多几种转移方式,我不说转移方程了,代码写得很漂亮。

    1 . 根据方向的不同要有一个费用B。

    2 . 加油站问题,即可以把油量转移成满,注意用不用建即可。

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 105
#define K 15
#define inf 0x3f3f3f3f
using namespace std;
const int dx[4]={1,0,-1,0};
const int dy[4]={0,1,0,-1};
struct Lux
{
	int k,x,y;
	Lux(int a,int b,int c):k(a),x(b),y(c){}
	Lux(){}
};

int map[N][N],id[N][N],cnt;
int n,p,A,B,C;
int dist[K][N][N];
bool in[K][N][N];
Lux s,t;

int spfa()
{
	int i,vx,vy,fee,fee2;
	queue<Lux>q;
	memset(dist,0x3f,sizeof(dist));
	dist[s.k][s.x][s.y]=0;
	in[s.k][s.x][s.y]=1;
	q.push(s);
	while(!q.empty())
	{
		Lux U=q.front();q.pop();in[U.k][U.x][U.y]=0;
		if(!U.k)continue;//没油了还跑什么
		for(fee=i=0;i<4;i++)
		{
			vx=U.x+dx[i];
			vy=U.y+dy[i];
			if(i==2)fee=B;/*往回走要多付的费用在这里处理了。*/
			if(!id[vx][vy])continue;
			if(!map[vx][vy])
			{
				fee2=C;/*新开油站,准确的说这里的思想是把全图都开成加油站,原加油站强制加油,新加油站不强制,但加油要多付钱*/
				if(U.k&&dist[U.k-1][vx][vy]>dist[U.k][U.x][U.y]+fee)
				{/*因为加油站强制加油,所以不是加油站才能这么转移。*/
					dist[U.k-1][vx][vy]=dist[U.k][U.x][U.y]+fee;
					if(!in[U.k-1][vx][vy])in[U.k-1][vx][vy]=1,q.push(Lux(U.k-1,vx,vy));
				}
			}
			else fee2=0;/*已经有原加油站了就不需要再付额外费用了*/
			
			if(dist[p][vx][vy]>dist[U.k][U.x][U.y]+A+fee+fee2)
			{/*加油的转移*/
				dist[p][vx][vy]=dist[U.k][U.x][U.y]+A+fee+fee2;
				if(!in[p][vx][vy])in[p][vx][vy]=1,q.push(Lux(p,vx,vy));
			}
		}
	}
	int ret=inf;
	for(i=0;i<=p;i++)ret=min(ret,dist[i][t.x][t.y]);
	return ret;
}

int main()
{
//	freopen("test.in","r",stdin);
	scanf("%d%d%d%d%d",&n,&p,&A,&B,&C);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&map[i][j]),id[i][j]=++cnt;
	s=Lux(p,1,1),t=Lux(0,n,n);
	printf("%d\n",spfa());
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值