初见安~这里是传送门:洛谷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;
}