题面

首先忽略掉题中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];
}
01-19 11:48