题目要求:时间最短,其次send数量最少,再是back数量最少。
之前错误解法:对于时间来说,因为若添加进来num节点时间最短,那么对a而言就是时间最短的。局部最优代表着结果最优。
但是对于send 和back这两个属性而言,局部的最小不代表全局的最小。
例如测试用例:
10 4 4 5
4 8 9 0
0 1 1
1 2 1
1 3 2
2 3 1
3 4 1
在错误的解法下取send back最小,就是从1->3,但是在该样例中节点四是0,所以其实从1->2->3,带给节点4的单车比从1->3带到节点4的单车更少。所以局部最优解不成立。
由于要从时间最短的路径里面选择,send back最小的。而沿途的send只能是累加,back数则是要考虑上一节点的情况,增加减少不变都可能。所以采取dfs回溯的方法,用nowPath记录当前路径,将起点压栈,判断相邻节点,满足时间最短的条件就压栈放入nowPath,以此类推。直到这个点所有的路径算完了,再nowPath.pop(),放出节点。
判断每一条符合时间最短条件下,到达sp时候,此时sp的send 和 back数,是否最优。最优则用当前路径nowPath更新栈结构minPath。
关键点:1、沿途节点只需要满足时间最短或者相等,至于send和back数只需要记录,不需要判断最优。只有在终点sp时,先判断时间最优,再判断send back数是否最优,最优就更新minPath。
2、关于每个节点send和back数量。定义,send是在路径上经过该节点时,需要PBMC送出多少车。back则是在路径上经过该节点后,需要送回PBMC多少车。
更新now节点的send back值条件如下:last节点是now的上一个节点
若now节点的单车数now.bike>=cmax/2,那么now节点是不需要额外从PBMC送车的,而now.send仅仅取决于上一个节点last需要PBMC送多少车(now.send=last.send)。而为了调整now最优,now需要送回PBMC now.bike-cmax/2这么多的单车,再加上从上一个节点last送回PBMC的车(now.back=last.back+now.bike-cmax/2)
若now节点的单车数now.bike<cmax/2,那么首先考虑
若从上一个节点送回的车last.back能够使的now节点最优last.bike>cmax/2-now.bike,那么now节点也不需要额外多送车送到上一个多少现在也多少,now.send=last.send。而上一个节点发回的车send.back一部分要留在now,now.back=send.back-(cmax/2-now.bike)
若从上一个节点送回的车不能满足now的需求,那么now就需要PBMC额外多送车,多送 除去last.back还不能填满cmax/2的部分now.send=last.send+cmax/2- last.back-now.bike。这种情况下到了now就都消耗完啦,没有多余的车了,now.back=0;
#include <stdio.h>
#include <vector>
#include <stack>
using namespace std;
int cap[501];//存储每个城市当前的单车数
struct E{
int next;
int time;
};
vector<E> edge[501];//邻接表存储图
stack<int> minPath;//满足条件的全局最优路径
stack<int> nowPath;//当前路径
int arrPath[501];//由于输出从0开始,出栈为逆序,用数组倒序遍历输出
int pathSize;//最优条件路径长,用于输出
int cmax,n,sp,m;
struct ans{
int minTime;
int send;
int back;
}ans[501];//当下这个节点的最小时间,在该路径上经过该节点的,从PMBC送出来的车,和送回PMBC的车
bool visited[501];//避免循环
void DFS(int num){
//遍历num的下一个节点
for(int i=0;i<edge[num].size();i++) {
int a = edge[num][i].next;
int b = edge[num][i].time;
if (visited[a] == false) {//若下一个节点不在路径上
if(a==sp){//若下一个节点就是最终的问题节点sp,也是回溯的最终
//首先是若sp节点没有被赋值过,或者从num到sp比之前的情况,时间更短
if (ans[a].minTime == 0 || ans[a].minTime > ans[num].minTime + b) {
ans[a].minTime = ans[num].minTime + b;//更改sp的最短时间情况
if (cap[a] >= cmax / 2) {//若sp这个点是满的
ans[a].send = ans[num].send;//sp不需要从PBMC送车,它的send仅仅取决于上一个节点num的需要多少车
ans[a].back = ans[num].back + cap[a] - cmax / 2;//从sp送回的车取决于,上一个节点送回来的,和自己多出来的
} else {//若sp这个节点是空的
if (ans[num].back >= cmax / 2 - cap[a]) {//但之前节点发到sp的够满足sp
ans[a].send = ans[num].send;//sp不需要送车啦
ans[a].back = ans[num].back + cap[a] - cmax / 2;//sp会消耗一部分num.back以满足自身
} else {//不能够满足
ans[a].send = ans[num].send + cmax / 2 - cap[a] - ans[num].back;//需要PBMC多送车啦
ans[a].back = 0;
}
}
nowPath.push(a);//将sp即a 入栈,由于已经到了回溯的终点
minPath=nowPath;//将当前的nowPath赋给最优路径minPath
nowPath.pop();//将a出栈
return;//返回
}
else if(ans[a].minTime==ans[num].minTime+b){//若时间相同,即到达sp有多条最优路径
int ifSend=0;//这个时候计算若从num到达sp,sp的send 和 back会是什么样子
int ifBack=0;
if(cap[a]>=cmax/2)
{
ifSend=ans[num].send;
ifBack=ans[num].back+cap[a]-cmax/2;
}else {
if (ans[num].back >= cmax / 2 - cap[a]) {
ifSend = ans[num].send;
ifBack = ans[num].back + cap[a] - cmax / 2;
} else {
ifSend = ans[num].send + cmax / 2 - cap[a] - ans[num].back;
ifBack = 0;
}
}
if(ifSend<ans[a].send){//如果送出的车少
ans[a].send=ifSend;
ans[a].back=ifBack;//更新send back
nowPath.push(a);
minPath=nowPath;
nowPath.pop();
return;
}
else if(ifSend==ans[a].send&&ifBack<ans[a].back)//送出车数量相同。送回车少
{
ans[a].back=ifBack;
nowPath.push(a);
minPath=nowPath;
nowPath.pop();
return;
}
return;
}
}else{//如果还没有到sp,考虑沿途节点只需要考虑 到这个节点的时间 是不是小于或者等于 最优时间,满足条件的情况下记录send back值
if (ans[a].minTime == 0 || ans[a].minTime >= ans[num].minTime + b) {
ans[a].minTime = ans[num].minTime + b;
if(cap[a]>=cmax/2)
{
ans[a].send=ans[num].send;
ans[a].back=ans[num].back+cap[a]-cmax/2;
}else{
if(ans[num].back>=cmax/2-cap[a]){
ans[a].send=ans[num].send;
ans[a].back=ans[num].back+cap[a]-cmax/2;
}else{
ans[a].send=ans[num].send+cmax/2-cap[a]-ans[num].back;
ans[a].back=0;
}
}
visited[a] = true;
nowPath.push(a);//将a压栈
DFS(a);//看a的下一节点
visited[a] = false;
nowPath.pop();//将a 出栈
}
}
}
}
}
int main(){
while(scanf("%d%d%d%d",&cmax,&n,&sp,&m)!=EOF){
edge[0].clear();
for(int i=1;i<=n;i++){
scanf("%d",&cap[i]);
edge[i].clear();
visited[i]=false;
ans[i].minTime=0;
ans[i].send=0;
ans[i].back=0;
}
pathSize=0;//之前都是初始化啦
for(int i=0;i<m;i++){
int a,b,t;
scanf("%d%d%d",&a,&b,&t);
E tmp;
tmp.next=a;
tmp.time=t;
edge[b].push_back(tmp);
tmp.next=b;
edge[a].push_back(tmp);
}//邻接表存储
ans[0].send=0;
ans[0].back=0;
ans[0].minTime=0;
nowPath.push(0);
visited[0]=true;
DFS(0);//从PBMC原点开始dfs回溯
printf("%d ",ans[sp].send);//最后ans[sp].send ans[sp].back就是所求
while(!minPath.empty())//minPath存储最优路径,但结果需要倒序输出
{
arrPath[pathSize++]=minPath.top();
minPath.pop();
}
for(int i=pathSize-1;i>0;i--){
printf("%d->",arrPath[i]);
}
printf("%d",arrPath[0]);
printf(" %d\n",ans[sp].back);
}
return 0;
}