题意:城市里有一些公共自行车站,每个车站的最大容量为一个偶数
C
m
a
x
Cmax
Cmax,如果该车站的自行车数量为
C
m
a
x
2
\frac{Cmax}{2}
2Cmax,那么就称该车站处于完美状态。
如果一个车站的容量是满的或者空的,控制中心就要携带或者从路上搜集一定数量的自行车前往该车站,以使问题车站及沿途所有车站处于完美状态。现给出
C
m
a
x
Cmax
Cmax、车站数目
n
n
n、问题车展编号
S
p
S_p
Sp、无向边数
M
M
M以及边权,求一条从控制中心到
S
p
S_p
Sp的最短路径,输出需要携带的自行车数目、路径、需要携带回中心的数目。如果路径有多条,则选择携带最少的;如果仍有多条,则选择带回中心最少的。
注意“调整过程需要在前往问题车站的过程中就调整完毕,返程不调整。
分析:
1.调整的过程是动态的,类似加油问题,而不能单纯的算总和。
英文原文是读不出动态过程的。
2.本题加入了第三标尺,第一标尺是距离,第二标尺是需求need,第三标尺是剩余remain。
注意:第二标尺更新时,第三标尺无条件更新,不要说是 Dijkstra+DFS就忘了这一点。
加油问题的逻辑:
在DFS从终点回溯到起点,计算路径总权值时,设need表示需要从中心带的数目,remain表示当前剩余的可用来补给的数目。
// 递归终点
if(v == 0) {
tmpPath.push_back(v);
int need = 0,remain = 0;
for(int i = tmpPath.size()-2; i >= 0; --i) {
int id = tmpPath[i];
if(weight[id] >= cmax / 2) {
// 如果超过了一半,可以用来补给
remain += weight[id] - cmax / 2;
} else {
// 当前不够
if(remain > cmax/2-weight[id]) {
// 携带的够补给,那么就补给
remain -= cmax/2-weight[id];
} else {
// 不够,补给剩下的要从 PBMC 带
need += (cmax/2-weight[id])-remain;
remain = 0;
}
}
}
完整代码:
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define INF 0x3fffffff
#define MAXN 550
using namespace std;
int cmax,n,sp,m;
int weight[MAXN];
int G[MAXN][MAXN];
int d[MAXN];
bool vis[MAXN] = {false};
int w[MAXN];
vector<int> pre[MAXN];
void Dijkstra(int s) {
// 0.初始化
fill(d,d+MAXN,INF);
d[0] = 0;
// n 次循环
for(int i = 0; i <= n; ++i) {
// 1. 从 V-S 中取出最近的
int u = -1,MIN = INF;
for(int j = 0; j <= n; ++j) {
if(vis[j] == false && d[j] < MIN) {
u = j;
MIN = d[j];
}
}
// 2.加入 S
if(u == -1) return ;
vis[u] = true;
//3.扩展 u
for(int v = 0; v <= n; ++v) {
if(vis[v] == false && G[u][v] != INF) {
if(d[u]+G[u][v] < d[v]) {
d[v] = d[u]+G[u][v];
pre[v].clear();
pre[v].push_back(u);
} else if( d[u]+G[u][v] == d[v]) {
pre[v].push_back(u);
}
}
}
}
}
vector<int> tmpPath,Path;
int minRemain = INF,minNeed = INF;
int bestValue;
int flag; // 0 是找最大值,1 是找最小值 (full)
void DFS(int v) {
// 递归终点
if(v == 0) {
tmpPath.push_back(v);
int need = 0,remain = 0;
for(int i = tmpPath.size()-2; i >= 0; --i) {
int id = tmpPath[i];
if(weight[id] >= cmax / 2) {
// 如果超过了一半,可以用来补给
remain += weight[id] - cmax / 2;
} else {
// 当前不够
if(remain > cmax/2-weight[id]) {
// 携带的够补给,那么就补给
remain -= cmax/2-weight[id];
} else {
// 不够,补给剩下的要从 PBMC 带
need += (cmax/2-weight[id])-remain;
remain = 0;
}
}
}
if(need < minNeed) {
minNeed = need;
Path = tmpPath;
minRemain = remain;
} else if(need == minNeed) {
if(remain < minRemain) {
minRemain = remain;
Path = tmpPath;
}
}
tmpPath.pop_back();
return ;
}
// 递归式,所有可走点 DFS
tmpPath.push_back(v);
for(int i = 0; i < pre[v].size(); ++i) {
DFS(pre[v][i]);
}
tmpPath.pop_back();
}
int main() {
fill(G[0],G[0]+MAXN*MAXN,INF);
scanf("%d%d%d%d",&cmax,&n,&sp,&m);
for(int i = 1; i <= n; ++i) {
scanf("%d",&weight[i]);
}
int si,sj,t;
for(int i = 0; i < m; ++i) {
scanf("%d%d%d",&si,&sj,&t);
G[si][sj] = G[sj][si] = t;
}
Dijkstra(0);
DFS(sp);
int len = Path.size();
printf("%d ",minNeed);
printf("0");
for(int i = len-2; i >= 0; --i) {
printf("->%d",Path[i]);
}
printf(" %d",minRemain);
return 0;
}