Public Bike Management(30) Case7更改后通过

题目要求:时间最短,其次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;
} 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值