传送门
汽车加油行驶
题意:给定网格,部分格点设有加油站,汽车驶至该格点必须加油。给定一辆车加满油的最大行驶距离,加一次油的花费,以及在任意点设置新油库的花费。求一辆满油车从左上角格点到右下角格点的最小花费。
I think
根据汽车的剩余油量建立分层图。
若油量不满,点< i,k>向点< i,top>连长度为A/A+C的边。
若油量非空,非油库点< i,k>向点< j,k-1>连长度为0的边(j为i在网格图中右/下方的相临点),向点< j,k-1>连长度为B的边(j为i在网格图中左/上方的相临点).而满油油库点< i,k>向点< j,k-1>连长度为0的边(j为i在网格图中右/下方的相临点),向点< j,k-1>连长度为B的边(j为i在网格图中左/上方的相临点).(非满油油库点必然加油).
Code
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int,int> pii;
const int sm = 11e4+10;
const int sn = 10*sm;
const int Inf = 0x3f3f3f3f;
int N,K,A,B,C,tot,Ans;
int to[sn],nxt[sn],hd[sm],c[sn];
int vis[sm],dis[sm];
int mp[11][102][102];
int tag[102][102];
int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v,int w) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=w;
}
bool chk(int u,int v) {
return (u>=1&&u<=N&&v>=1&&v<=N);
}
void Dijkstra(int s,int St) {
for(int i=1;i<=s;++i) dis[i]=Inf;
priority_queue<pii,vector<pii>,greater<pii> >Que;
Que.push(make_pair(dis[St]=0,St));
pii t;
while(!Que.empty()) {
t=Que.top(),Que.pop();
if(vis[t.second])continue;
vis[t.second]=1;
for(int i=hd[t.second];i;i=nxt[i])
if(!vis[to[i]]&&dis[to[i]]>dis[t.second]+c[i]) {
dis[to[i]]=dis[t.second]+c[i];
Que.push(make_pair(dis[to[i]],to[i]));
}
}
}
int main() {
int cnt=0,u;
scanf("%d%d%d%d%d",&N,&K,&A,&B,&C);
for(int k=0;k<=K;++k)
for(int i=1;i<=N;++i)
for(int j=1;j<=N;++j) {
if(k==0) scanf("%d",&tag[i][j]);//tag[i][j]==1该点设置了油库
mp[k][i][j]=++cnt;
}
for(int k=K;k>=0;--k)
for(int i=1;i<=N;++i)
for(int j=1;j<=N;++j) {
if(k!=K) //油量不满
Add(mp[k][i][j],mp[K][i][j],A+C*(tag[i][j]^1));
if((!tag[i][j]&&k>0)||(k==K)) { //油量非空 //是油库且非满油 必然加油
if(chk(i,j+1)) Add(mp[k][i][j],mp[k-1][i][j+1],0);
if(chk(i+1,j)) Add(mp[k][i][j],mp[k-1][i+1][j],0);
if(chk(i,j-1)) Add(mp[k][i][j],mp[k-1][i][j-1],B);
if(chk(i-1,j)) Add(mp[k][i][j],mp[k-1][i-1][j],B);
}
}
Dijkstra(cnt,mp[K][1][1]);
Ans=Inf;
for(int i=0;i<=K;++i)
Ans=Min(Ans,dis[mp[i][N][N]]);
printf("%d\n",Ans);
return 0;
}