首先忽略掉题中k的限制(油量的限制);
那么很清楚的发现:对于一个点(i,j);向(i+1,j),(i,j+1)建立一条容1费0的边;向(i-1,j),(i,j-1)建立一条容1费B的边;
很清楚的:从(1,1)到(n,n)跑费用流就好了;
那么加上k的限制呢?
由于k很小,所以我们可以建立分层图(分k+1层);
第0层表示剩余油量是k,第k层表示剩余油量是0;
那么当此处没有加油站时,我们要把第i层(0<=i<K)连向第i+1层的下一个位置,第k层则直接向第1层同一位置连上一条容1费A+C的边;
当此处有加油站时,我们从第1层到第k层都向第0层连上一个容1费A的边,然后第0层再向别的点连边;
然后原点是(1,1); 所有层的(n,n)向汇点连一条容1费0的边,跑费用流;
我们注意到,所有边的容量都是1,那么便不用跑最小费用最大流,一边SPFA/Dijkstra就可以了(可以双端队列优化);
#include <bits/stdc++.h> #define inc(i,a,b) for(register int i=a;i<=b;i++) using namespace std; class littlestar{ public: int to; int nxt; int w; void add(int u,int v,int gg); }star[2000010]; int head[2000010],cnt; inline void littlestar::add(int u,int v,int gg) { to=v; nxt=head[u]; w=gg; head[u]=cnt; } int n,k,A,B,C,tot; int query[101][101],a[101][101]; int S,T; deque<int> q; int dis[1000010],vis[1000010]; void SPFA() { memset(dis,0x3f,sizeof(dis)); q.push_back(S); dis[S]=0; while(q.size()){ int u=q.front(); q.pop_front(); vis[u]=0; for(register int i=head[u];i;i=star[i].nxt){ int v=star[i].to; if(dis[v]>dis[u]+star[i].w){ dis[v]=dis[u]+star[i].w; if(vis[v]==0){ vis[v]=1; if(q.size()&&dis[v]>dis[q.front()]) q.push_back(v); else q.push_front(v); } } } } } int main() { cin>>n>>k>>A>>B>>C; inc(i,1,n) inc(j,1,n){ scanf("%d",&a[i][j]); ++tot; query[i][j]=tot; } S=tot*(k+1)+1,T=tot*(k+1)+2; star[++cnt].add(S,1,0); inc(i,1,n){ inc(j,1,n){ int tmp=query[i][j]; if(a[i][j]){ inc(o,1,k){ star[++cnt].add(o*tot+tmp,tmp,A); } } inc(o,0,k-1){ if(a[i][j]&&o) break; if(i>1) star[++cnt].add(o*tot+tmp,(o+1)*tot+query[i-1][j],B); if(j>1) star[++cnt].add(o*tot+tmp,(o+1)*tot+query[i][j-1],B); if(i+1<=n) star[++cnt].add(o*tot+tmp,(o+1)*tot+query[i+1][j],0); if(j+1<=n) star[++cnt].add(o*tot+tmp,(o+1)*tot+query[i][j+1],0); } star[++cnt].add(k*tot+tmp,tmp,A+C); } } inc(i,0,k){ star[++cnt].add(i*tot+query[n][n],T,0); } SPFA(); cout<<dis[T]; }