给个负责任的OJ:汽车加油
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 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
这个题,完全不像是网络流的类型啊,然后一看就不会写
学了题解之后,发现图论的很多思路和动态规划也是一样一样的,和搜索也是一样的
在(x,y)二维表示坐标,状态表示不够全面的时候,往往在加一维来表示
所以,本题是一个最短路问题
状态的表示量为(i,j,k),其中(i,j)为坐标,k为当前在该格子中的剩余的油量
然后,根据题中的数据来添加状态转移的边和权值
推荐一篇博客:
http://blog.csdn.net/willinglive/article/details/38148805
这个题,还有1个坑点,在于我们需要对maxn的值进行计算
n最大是100,那么maxn是多少?
算大了无所谓(只要不开得过大,超过题目中所给的最大空间)
算小了会是莫名其妙的wa
有可能的达到的范围是:100*100*100
那么需要1e6的数组来保存每个节点的能到的最小值
代码如下:
用SPFA算法跑的最短路,大概复杂度是O(nlogn),是基本可以AC的
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+500;
const int maxe=2e6+500;
const int maxsize=1e6+500;
const int INF=1<<30;
struct Edge{
int u,w,v,nxt;
}edge[maxe];
int cnt,q[maxsize];
int mp[110][110];
int a,b,c,k,n,s,t;
int C[2]={0,0};
int dx[]={-1,0,1,0};
int dy[]={0,-1,0,1};
int getpoint(int k,int x,int y){
return k*n*n+x*n+y;
}
int Head[maxn],d[maxn];
bool vis[maxn];
void addedge(int u,int v,int w){
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].nxt=Head[u];
Head[u]=cnt++;
}
void SPFA(){
memset(vis,0,sizeof(vis));
for(int i=0;i<=t;i++) d[i]=INF;
d[s]=0;
vis[s]=true;
int l=0,r=0;
q[r++]=s;
while(l<r){
int u=q[l%maxsize];
l++;
vis[u]=0;
for(int i=Head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].v;
if (edge[i].w+d[u]<d[v]){
d[v]=edge[i].w+d[u];
if (!vis[v]){
vis[v]=1;
q[r%maxsize]=v;
r++;
}
}
}
}
return;
}
int main(){
freopen("input.txt","r",stdin);
while(scanf("%d%d%d%d%d",&n,&k,&a,&b,&c)!=EOF){
//cout<<n<<" "<<k<<endl;
s=getpoint(k,1,1);
t=getpoint(k,n,n);
C[0]=c;C[1]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&mp[i][j]);
memset(Head,-1,sizeof(Head));
cnt=0;
for(int l=0;l<=k;l++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
int temp=getpoint(l,i,j);
if (l==0) addedge(temp,getpoint(k,i,j),a+C[mp[i][j]]);
//走不动了,油量不够了,只能在原地加油
//原地加油分成两种情况,有油库和没有油库
else{
for(int dir=0;dir<4;dir++){
int x=i+dx[dir],y=j+dy[dir];
if (x>=1&&x<=n&&y>=1&&y<=n){
if (dir<=1){
if (mp[x][y]) addedge(temp,getpoint(k,x,y),b+a);
else addedge(temp,getpoint(l-1,x,y),b);
//往左上走,位置变了
//有油库我们就可以选择加油
}
else{
if (mp[x][y]) addedge(temp,getpoint(k,x,y),a);
else addedge(temp,getpoint(l-1,x,y),0);
//往右下走,位置变了
//有油库我们就可以选择加油
}
}
}
}
if (l!=k) addedge(temp,getpoint(k,i,j),a+C[mp[i][j]]);
//如果油箱不满,也可以加油
}
SPFA();
int ans=INF;
for(int l=0;l<=k;l++)
if (d[getpoint(l,n,n)]<ans)
ans=d[getpoint(l,n,n)];
cout<<ans<<endl;
}
return 0;
}