题意
城市里有一些公共自行车站,每个车站的自行车最大容量为一个偶数Cmax,且如果一个车站中自行车的数量恰好为Cmax/2,那么称该车站处于“完美状态”。而如果一个车站容量是满的或是空的,那么控制中心(PBMC)就会携带或从路上收集一定数量的自行车前往该车站,以使问题车站及沿途所有车站都达到“完美状态”。
现在给出Cmax、车站数目N(不含控制中心PBMC)、问题车站编号Sp、无向边数M及边权,求一条从 PBMC (记为0号)到达问题车站Sp的最短路径,输出需要从PBMC携带的自行车数目、最短路径、到达问题车站后需要带回的自行车数目。如果最短路径有多条,那么选择从PBMC携带的自行车数目最少的;如果仍然有多条,那么选择最后从问题车站带回的自行车数目最少的。
注意:沿途所有车站的调整过程必须在前往问题车站的过程中就调整完毕,带回时不再调整。
思路
把每个点的点权(自行车数目)都减去Cmax/2,这样就可以用点权的正负来直接判断当前车站是需要补给还是需要带走额外的车辆。由于需要输出应从PBMC携带的自行车数目与从问题车站带回的自行车数目,因此对每个顶点来说需要增加两个属性:从PBMC到当前车站必须携带的自行车数目Send以及到达当前车站时手上多余的自行车数目Take。显然,如果当前车站u的点权为正,说明需要从该车站额外带走自行车,因此新的Take等于旧的Remain加上weight[u];而如果当前车站u的点权为负,说明当前车站需要补给自行车的数量为abs(weight[u]),此时如果Take大于0,就可以用来补给当前车站,但如果Take不够完全补给,剩余部分需要从PBMC携带,故Send增加这个数值。
显然,本题可以使用Dijkstra + DFS的写法求解。具体做法是,先使用Dijkstra求出所有最短路径,然后用DFS从这些最短路径中选出need最小的( need相同时选择remain最小的)。最短路径的条数既可以在Dijkstra部分顺便求出,也可以在DFS中边界条件处进行累计。
注意点
- 题意中最重要的“陷阱”从起点PBMC出发到达问题站点Sp的过程中就要把路径上的所有站点都调整为最优状态,此后不再调整;并且决策时以距离作为第一标尺,当最短距离相同时选择从起点PBMC携带最少数量自行车的路径,在此基础上如果携带量最少的路径仍然有多条,则选择到达问题站点Sp后需要带回自行车的数量最少的路径。
- 本题不能只使用Dijkstra来解决,因为minSend和minTake在路径上的传递不满足最优子结构(不是简单的相加过程)。也就是说,只有当所有路径都确定后,才能去选择最小的need和最小的remain。
- DFS计算点权之和时,注意不需要加上PBMC即\(0\)号点的点权。
const int N=510;
vector<PII> g[N];
int dist[N];
bool vis[N];
vector<int> pre[N];
vector<int> optpath;
int minSend=INF,minTake=INF;
int c[N];
int Cmax,n,m,st,ed;
void dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[0]=0;
priority_queue<PII,vector<PII>,greater<PII> > heap;
heap.push({0,0});
while(heap.size())
{
int t=heap.top().se;
heap.pop();
if(vis[t]) continue;
vis[t]=true;
for(int i=0;i<g[t].size();i++)
{
int j=g[t][i].fi,w=g[t][i].se;
if(dist[j]>dist[t]+w)
{
dist[j]=dist[t]+w;
pre[j].clear();
pre[j].pb(t);
heap.push({dist[j],j});
}
else if(dist[j] == dist[t]+w)
pre[j].pb(t);
}
}
}
void dfs(int u,vector<int> &path)
{
if(u == 0)
{
int send=0,take=0;
for(int i=path.size()-2;i>=0;i--)
{
int j=path[i];
if(c[j] > Cmax/2) take+=c[j]-Cmax/2;
else
{
int t=Cmax/2-c[j];
if(take > t) take-=t;
else
{
send+=t-take;
take=0;
}
}
}
if(send < minSend)
{
minSend=send;
minTake=take;
optpath=path;
}
else if(send == minSend && take < minTake)
{
minTake=take;
optpath=path;
}
return;
}
for(int i=0;i<pre[u].size();i++)
{
int j=pre[u][i];
path.push_back(j);
dfs(j,path);
path.pop_back();
}
}
int main()
{
cin>>Cmax>>n>>ed>>m;
for(int i=1;i<=n;i++) cin>>c[i];
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a].pb({b,c});
g[b].pb({a,c});
}
dijkstra();
vector<int> path;
path.push_back(ed);
dfs(ed,path);
cout<<minSend<<' ';
for(int i=optpath.size()-1;i>=0;i--)
if(i) cout<<optpath[i]<<"->";
else cout<<optpath[i];
cout<<' '<<minTake<<endl;
//system("pause");
return 0;
}