题目链接:戳我
分层图最短路问题。
我们设(dp[i][k])表示节点为i,现在还有k条边可以走的状态。然后跑一个最短路就行了。因为是不定项更新,所以考虑spfa。
注意到了加油站是强制消费。然后分向上下左右四个方向走和原地建加油站五种情况来讨论。
注意最后更新答案的时候k==0不要忘了考虑!!(要不然就会像我一样WA掉一个点)
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<algorithm>
#include<queue>
#define MAXN 100010
using namespace std;
int n,k,a,b,c,tot,edge_number,ans=2147483647;
int m[MAXN],dp[MAXN][20],id[210][210],done[MAXN][20],head[MAXN*110];
struct Node{int u,d;};
struct Edge{int nxt,to,dis;}edge[MAXN*110];
inline void add(int from,int to,int dis)
{
edge[++edge_number].nxt=head[from];
edge[edge_number].to=to;
edge[edge_number].dis=dis;
head[from]=edge_number;
}
inline void solve()
{
queue<Node>q;
memset(dp,0x3f,sizeof(dp));
q.push((Node){id[1][1],k});
dp[id[1][1]][k]=0;
done[id[1][1]][k]=1;
while(!q.empty())
{
int u=q.front().u,d=q.front().d;
q.pop();done[u][d]=0;
if(d!=0)
{
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to,yu=d-1,cur_dis=dp[u][d]+edge[i].dis;
if(m[v]==1) yu=k,cur_dis+=a;
if(cur_dis<dp[v][yu])
{
dp[v][yu]=cur_dis;
if(!done[v][yu])
q.push((Node){v,yu}),done[v][yu]=1;
}
}
}
int cur_dis=dp[u][d]+c+a;
if(m[u]==1) cur_dis-=c;
if(cur_dis<dp[u][k])
{
dp[u][k]=cur_dis;
if(!done[u][k])
q.push((Node){u,k}),done[u][k]=1;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d%d%d",&n,&k,&a,&b,&c);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
id[i][j]=++tot,scanf("%d",&m[id[i][j]]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=1) add(id[i][j],id[i-1][j],b);
if(j!=1) add(id[i][j],id[i][j-1],b);
if(i!=n) add(id[i][j],id[i+1][j],0);
if(j!=n) add(id[i][j],id[i][j+1],0);
}
}
solve();
for(int i=0;i<=k;i++) ans=min(ans,dp[id[n][n]][i]);
printf("%d
",ans);
return 0;
}