洛谷·[网络流24题]汽车加油行驶问题

初见安~这里是传送门:洛谷P4009 汽车加油行驶问题

题解

思路比较清奇……往费用流想是真的想不动。但如果抛开网络流的标签,其实可以发现——这应该是一个最短路分层图

我们把剩余油量作为分层的标准,有0~K层,每一层都是一个地图。接下来来看各个操作——【相互可以有交集,主要是理解】

1、下一个点v是加油站,边权为A连向第K层的点v。否则连向当前下一层的点v。

2、下一步是逆行,边权要加上B。

3、当前在第0层,没油了,原地放一个加油站,边权为A+C连向第K层的当前点。

没了。建图过后跑最短路吧。答案取每一层的终点的最小值。

有个问题我迷惑了一会儿——题目说遇油库则加满油并付费,那如果我当前油就已经满了呢?后来发现其实这种情况不存在……就没事了。【人类迷惑问题。

那么这为什么是一个网络流的题呢……???其实硬用费用流也能做……容量为1,费用为上述对应边权,跑最小费用流……

上代码——

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 200005
#define maxm 1000005
using namespace std;
typedef long long ll;
const ll INF = 1e14;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

struct edge {int to, w, nxt;} e[maxm];
int head[maxn], k = 0;
void add(int u, int v, int w) {e[k] = {v, w, head[u]}; head[u] = k++;}


int n, K, A, B, C;
bool oil[maxn];
int id(int i, int j) {return n * (i - 1) + j;}//二维的标号,i行j列
int id2(int i, int j) {return n * n * i + j;}//第i层,第j个点(j在1~n*n)
bool in(int x, int y) {return 0 < x && x <= n && 0 < y && y <= n;}

void init(int u, int v, int w) {
	bool flag = oil[v];
	if(flag) {for(int i = 1; i < K; i++) add(id2(i, u), id2(K, v), w);}//强制加油 
	else {for(int i = K; i > 0; i--) add(id2(i, u), id2(i - 1, v), w);}//否则下一层
}

priority_queue<pair<int, int> > q;
ll dis[maxm];
int S;
bool vis[maxm];
void dij() {//最短路
	q.push(make_pair(0, S));
	while(q.size()) {
		register int u = q.top().second; q.pop();
		if(vis[u]) continue; vis[u] = true;
		for(int i = head[u], v; ~i; i = e[i].nxt) {
			v = e[i].to; if(dis[u] + e[i].w < dis[v]) {
				dis[v] = dis[u] + e[i].w, q.push(make_pair(-dis[v], v));
			}
		}
	}
}

signed main() {
	memset(head, -1, sizeof head);
	n = read(), K = read(), A = read(), B = read(), C = read();
	for(int i = 1; i <= n * n; i++) oil[i]= read();//注意这里用的编号方式
	
	for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) {
		register int u = id(i, j);//因为每一层是二维的,加上层就三维了,所以id不太好标
		if(in(i + 1, j)) init(u, id(i + 1, j), oil[id(i + 1, j)] * A);//如上文建图
		if(in(i, j + 1)) init(u, id(i, j + 1), oil[id(i, j + 1)] * A);
		if(in(i - 1, j)) init(u, id(i - 1, j), oil[id(i - 1, j)] * A + B);
		if(in(i, j - 1)) init(u, id(i, j - 1), oil[id(i, j - 1)] * A + B);
		for(int l = 0; l <= K; l++) add(id2(l, u), id2(K, u), C + A);
	}
	
	
	ll ans = INF; for(int i = 0; i <= n * n * (K + 1); i++) dis[i] = INF;
	S = id2(K, id(1, 1)), dis[S] = 0;
	dij(); 
	
	for(int i = 0; i <= K; i++) ans = min(ans, dis[id2(i, id(n, n))]);//取min
	printf("%lld\n", ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值