汽车加油行使问题DP(算法设计与分析-王晓东)

题目描述 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 Description

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

输出描述 Output Description

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

样例输入 Sample Input

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

样例输出 Sample Output

12

终点的一条所付费用最少的行驶路线。

 

解题思路:定义dp[i][j][2]数组,dp[i][j][0]代表从起点到(i,j)所花费的最小费用,dp[i][j][1]代表从起点到(i,j)还能行使的网格边数,于是有初始状态dp[1][1][0]=0,dp[1][1][1]=K。

其次有数组dir[4][3],dri[i][0]代表x方向,dir[i][1]代表y方向,dir[i][2]代表行使所花费的费用

对于任意一个点(i,j)能够到达它的点为上下左右四个点(i-1,j)(i+1,j) (i,j-1)(i,j+1)。

因此有状态转移方程为dp[i][j][0]=min(上下左右四个方向到达(i,j)的费用)
费用的计算细节详见代码
 

#include 
#include 
#define inf 0x3f3f3f3f
#define maxn 105
using namespace std;
int N,K,A,B,C;
//dp二维数组代表答案更新数组,grap代表所输入的二维矩阵 
int dp[maxn][maxn][2],grap[maxn][maxn];

int main()
{
	cin>>N>>K>>A>>B>>C;
	
	for(int i=1;i<=N;i++)
	  for(int j=1;j<=N;j++)cin>>grap[i][j];
	
	//方向数组其中:dir[j][3]:j=0->3:分别代表左,上,右,下
	//dir[j][0]为x坐标的增加情况,dir[j][1]为y坐标的增加情况
	//dir[j][2]为是否需要增加费用 ,即是否进行了坐标减小的走法 
	int dir[4][3]={{0,-1,0},{-1,0,0},{0,1,B},{1,0,B}};

	memset(dp,inf,sizeof(dp));//dp数组初始化为极大值 
    dp[1][1][0]=0;dp[1][1][1]=K;//初始化状态在(1,1)处花费应为0,还能走的步数为K 
    
    bool ok=true;//ok为true是代表还能进行更新 
    while(ok)
    {
    	ok=false;
    	
    	for(int i=1;i<=N;i++)
    	{
    		for(int j=1;j<=N;j++)
    		{
    			if(i+j==2)continue;//当为起点时略过 
    			
    			int now0=dp[i][j][0];//当前坐标的花费 
    			int now1=dp[i][j][1];//当前坐标还能行走的步数 
    			
    			for(int k=0;k<4;k++)//考虑与当前坐标相邻的四个方向的状态,称做QD 
    			{
    				int dx=i+dir[k][0];//QD的横坐标计算 
    				int dy=j+dir[k][1];//QD的纵坐标计算 
    				int db=dir[k][2];//从(dx,dy)到(i,j)是否需要额外增加费用,即从QD到现在是否需要额外费用 
    				
    				if(dx<1||dx>N||dy<1||dy>N)continue;//当在地图外时忽略该种状态 
    				
    				int newn0=dp[dx][dy][0]+db;//利用QD更新后的dp[i][j]的费用 
    				int newn1=dp[dx][dy][1]-1;//利用QD更新后的dp[i][j]还能走的步数 
    				if(grap[i][j])//当(i,j)为一个油库时,需要进行如下处理 
    				{			
    					newn0+=A;
    					newn1=K;
					}
					//当从QD到(i,j)后能走的步数为0时,我们需要在(i,j)处增设一个油库
					//但(i,j)不能是终点(N,N),因为终点不能增设油库 
					if(newn1==0&&(i+j!=2*N))
					{
						newn0+=(A+C);
						newn1=K;
					}
					//当更新后的状态优于更新前的状态执行本次更新 
					if(newn0

 

你可能感兴趣的:(动态规划,dp专题,算法设计与分析(王晓东))