网络流24题-汽车加油行驶问题
首先忽略掉题中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];
}
众人皆醉我独醒,举世皆浊我独清

浙公网安备 33010602011771号