给出
n
×
n
n×n
n×n的格点图,起点在
(
1
,
1
)
(1,1)
(1,1),终点在
(
n
,
n
)
(n,n)
(n,n)。一开始的油量为
k
k
k,然后每次沿着一条边移动,会消耗
1
1
1的单位的油。若
X
X
X或者
Y
Y
Y增大则不需要费用,否则需要
B
B
B的费用。另外其中的一些点是油库,遇到了就会把油加满,但要多支付
A
A
A的费用。另外也可以当场建立一个新的油库,这个需要
C
C
C的费用。
先把图建出来,然后跑最短路的时候加入状态。转移的时候连着状态一起转移。
如果你跑dij的话,复杂度接近
O
(
n
k
l
o
g
n
)
O(nklogn)
O(nklogn)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=1e2+7;
int n,k,A,B,C;
int mp[N][N],tot=0;
int up[N*N];
struct edge { int v,w; };
vector<edge> go[N*N];
void add(int u,int v,int w) { go[u].push_back({v,w}); }
struct node {
int U,K;
node() {}
node(int U,int K):U(U),K(K) {}
};
int dis[N*N][11];
bool vis[N*N][11];
void spfa() {
queue<node> q;
memset(dis,63,sizeof(dis));
dis[1][k]=0;
q.push({1,k});
while(!q.empty()) {
node cur=q.front();
q.pop();
int U=cur.U;
int K=cur.K;
vis[U][K]=0;
if(K>=1) {
for(auto &e:go[U]) {
int V=e.v;
int w=e.w;
if(up[V]==1) {
if(dis[U][K]+w+A<dis[V][k]) {
dis[V][k]=dis[U][K]+w+A;
if(!vis[V][k]) {
q.push({V,k});
vis[V][k]=1;
}
}
}
else {
if(dis[U][K]+w<dis[V][K-1]) {
dis[V][K-1]=dis[U][K]+w;
if(!vis[V][K-1]) {
q.push({V,K-1});
vis[V][K-1]=1;
}
}
}
}
}
if(dis[U][k]>dis[U][K]+A+C) {
dis[U][k]=dis[U][K]+A+C;
if(!vis[U][k]) {
q.push({U,k});
vis[U][k]=1;
}
}
}
}
int main() {
scanf("%d%d%d%d%d",&n,&k,&A,&B,&C);
int col;
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
scanf("%d",&col);
mp[i][j]=++tot;
if(col) up[tot]=1;
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(j<n) add(mp[i][j],mp[i][j+1],0);
if(i<n) add(mp[i][j],mp[i+1][j],0);
if(i>1) add(mp[i][j],mp[i-1][j],B);
if(j>1) add(mp[i][j],mp[i][j-1],B);
}
}
spfa();
ll ans=1e15;
for(int i=0;i<=k;i++)
ans=min(ans,1LL*dis[n*n][i]);
printf("%lld\n",ans);
return 0;
}