题目链接
题意:
有一个n*n的网格,每次车沿格子走一格,有些格子有加油站,路过加油站必须加油,可以额外花c元在任意一个点加油,每次加了油之后能走k步,向上或者向左走要额外花b元,问从(1,1)到(n,n)的最小花费。
题解:
分k+1层建图,表示上次加了油之后当前已经走了k步,我们规定层数为0-k,图上的边权根据题意都比较容易确定,这里就简单介绍一下建边方法。对于每一层,如果到了一个加油站,那么边要从每一层向第0层的对应点连边;如果到达的点不是加油站,就往下一层的对应点连边。如果是第k层,那么只可以通过原有加油站或者多花c元在任意一点加油的方法再返回到第0层。之后就是从第0层的(1,1)节点开始跑最短路就行了。
代码:
c++
#include <bits/stdc++.h>
using namespace std;
int n,k,b,c,d,hed[2000010],cnt,dis[2000010],num[110][110],shu;
int ju[110][110],vis[2000010],ans=2e9;
struct node
{
int to,dis,next;
}a[5000010];
priority_queue<pair<int,int> > q;
inline void add(int from,int to,int dis)
{
a[++cnt].to=to;
a[cnt].dis=dis;
a[cnt].next=hed[from];
hed[from]=cnt;
}
int main()
{
scanf("%d%d%d%d%d",&n,&k,&b,&c,&d);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
scanf("%d",&ju[i][j]);
num[i][j]=++shu;
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
if(j>1)
{
int x=num[i][j],y=num[i][j-1],z=n*n;
for(int l=0;l<=k-1;++l)
{
if(ju[i][j-1]!=1)
{
add(x+l*z,y+(l+1)*z,c);
add(x+l*z,x,b+d);
}
else
{
add(x+l*z,y,b+c);
add(x+l*z,x,b+d);
}
}
add(x+k*z,x,b+c+d);
}
if(j<n)
{
int x=num[i][j],y=num[i][j+1],z=n*n;
for(int l=0;l<=k-1;++l)
{
if(ju[i][j+1]!=1)
add(x+l*z,y+(l+1)*z,0);
else
add(x+l*z,y,b);
}
add(x+k*z,x,b+d);
}
if(i>1)
{
int x=num[i][j],y=num[i-1][j],z=n*n;
for(int l=0;l<=k-1;++l)
{
if(ju[i-1][j]!=1)
add(x+l*z,y+(l+1)*z,c);
else
add(x+l*z,y,b+c);
}
add(x+k*z,x,b+d);
}
if(i<n)
{
int x=num[i][j],y=num[i+1][j],z=n*n;
for(int l=0;l<=k-1;++l)
{
if(ju[i+1][j]!=1)
add(x+l*z,y+(l+1)*z,0);
else
add(x+l*z,y,b);
}
add(x+k*z,x,b+d);
}
}
}
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(vis[x])
continue;
vis[x]=1;
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(dis[y]>dis[x]+a[i].dis)
{
dis[y]=dis[x]+a[i].dis;
q.push(make_pair(-dis[y],y));
}
}
}
int x=num[n][n],y=n*n;
for(int i=0;i<=k;++i)
ans=min(ans,dis[i*y+x]);
printf("%d\n",ans);
return 0;
}