1018 Public Bike Management (30 分)

There is a public bike service in Hangzhou City which provides great convenience to the tourists from all over the world. One may rent a bike at any station and return it to any other stations in the city.

The Public Bike Management Center (PBMC) keeps monitoring the real-time capacity of all the stations. A station is said to be in perfect condition if it is exactly half-full. If a station is full or empty, PBMC will collect or send bikes to adjust the condition of that station to perfect. And more, all the stations on the way will be adjusted as well.

When a problem station is reported, PBMC will always choose the shortest path to reach that station. If there are more than one shortest path, the one that requires the least number of bikes sent from PBMC will be chosen.
在这里插入图片描述
The above figure illustrates an example. The stations are represented by vertices and the roads correspond to the edges. The number on an edge is the time taken to reach one end station from another. The number written inside a vertex S is the current number of bikes stored at S. Given that the maximum capacity of each station is 10. To solve the problem at S3​, we have 2 different shortest paths:
在这里插入图片描述
在这里插入图片描述 这个题可以抽象为最短路径问题,不过它具有三个标尺,第一标尺是最短时间,在题中表现为两点之间连线的权重;第二标尺为从需要从起点带到终点的自行车数量,第三标尺为需要从终点带回起点的自行车数量。需要注意的是,在从起点到终点的路径上,如果后一个点的自行车数量不够,可以用之前点多余的自行车数量进行补充,但是如果是前一个点的自行车数量不够,不能用后面点多余的自行车数量补充,如果它无法从之前点获得补充,那只能从起点进行补充,举个例子:每个点容量为10,一条路径为起点->0->10,那么这种情况下,需要从起点带出5辆自行车补到第一个点,并且需要从第二个点带5辆自行车回起点。
对于这个题,可以想到用Dijkstra算法+DFS的方法进行求解,先用Dijkstra算法求出只考虑第一标尺的最短路径,然后再利用DFS遍历每条路径,遍历到起点时即可获得一条完整路径,再依据规则对这条完整路径求解要带回和要带出的自行车数量,再按标尺优先级进行判断即可。
代码如下:

#include<iostream>
#include<vector>
#include<limits.h>
#include<algorithm>
#define MAXV 502
using namespace std;

int take_out = INT_MAX; //带出去的自行车数量
int bring_in = INT_MAX;//带回来的自行车数量
int INF = INT_MAX;
int weight[MAXV];   //点权数组
int G[MAXV][MAXV];  //图的邻接矩阵
int d[MAXV];    //各站与主站0之间的最短距离
vector<int> pre[MAXV];    //保存最短路径中每个结点的前驱结点
vector<bool> visited(MAXV,false);
vector<int> tempPath;   //用于在DFS中保存路径中的结点
vector<int> BestPath;   //存放最终选择的路径

void Dijkstra(int s,int N);
void DFS(int v,int s,int cap);
int main()
{
    //带点权的最短路径问题,Dijkstra算法
    int C,N,P,M;    //C是结点最大容量,N是总结点数,P是出问题的结点,M是边数
    cin >> C >> N >> P >> M;
    for(int i=1;i<=N;i++)
    {
        cin >> weight[i];
        weight[i] -= C/2;   //直接减去容量一半,之后利用正负进行判断
    }
    fill(G[0],G[0]+MAXV*MAXV,INF);
    for(int i=0;i<M;i++)
    {
        int v1,v2,dis;
        cin >> v1 >> v2 >> dis;
        G[v1][v2] = G[v2][v1] = dis;
    }
    //现在获取了所有的数据,下面利用Dijkstra找出最短路径(仅距离最短)
    Dijkstra(0,N);
    
    //找出了最短的路径,下面对这些最短路径进行DFS遍历(因为路程最短可能不唯一),找出符合题意的一条
    DFS(P,0,C);
    
    cout<<take_out<<" ";
    for(int i=BestPath.size()-1;i>=0;i--)
    {
        cout<<BestPath[i];
        if(i != 0)
            cout<<"->";
    }
    cout<<" "<<bring_in<<endl;
    
    return 0;
}

void Dijkstra(int s,int N)  //s表示起点,N表示总结点数
{
    fill(d,d+MAXV,INF);
    d[s] = 0;
    for(int i=0;i<=N;i++)
    {
        int u = -1,MIN = INF;
        for(int j=0;j<=N;j++)
        {
            if(!visited[j] && d[j]<MIN)
            {
                MIN = d[j];
                u = j;
            }
        }
        
        if(u == -1) return;
        visited[u] = true;
        for(int v=0;v<=N;v++)
        {
            if(!visited[v] && G[u][v]!=INF)
            {
                if(d[u]+G[u][v] < d[v])
                {
                    d[v] = d[u] + G[u][v];
                    pre[v].clear();
                    pre[v].emplace_back(u);
                }
                else if(d[u]+G[u][v] == d[v])
                    pre[v].emplace_back(u);
            }
        }
    }
}

void DFS(int v,int s,int cap)   //v表示当前访问的结点,s表示起点
{
    if(v == s)  //访问到了起点,现在我们有了这条完整的路径,那么就计算这条路径上的可用信息
    {
        //此时tempPath是完整路径(除了起点,因为起点不是自行车存放点)
        int bike = 0;
        int total_out = 0;     //从总站带出的自行车总量
        int total_in = 0;    //带回总站的自行车总量
        int size = tempPath.size();
        
        //从总站开始遍历到目标站
        for(int i=size-1;i>=0;i--)
        {   
            //获得当前站点的自行车数量
            int w = weight[tempPath[i]];
            if(w > 0)   //需要拿走自行车
                total_in += w;
            else    //需要带来自行车
            {
                //先看从之前站累积的自行车能不能填上这个窟窿
                if(total_in >= abs(w))   
                    total_in -= abs(w);  
                else
                {
                    total_out += abs(w) - total_in;
                    total_in = 0;
                }
            }
        }
        //处理完一条路之后,就得到了需要从总站拿出或需要带回总站的自行车数量
        //因为该题目起始有两个标尺,如果最终需要带出自行车,则选择带出最少的那条路径
        //如果最终不用带出自行车,则要选择带回自行车最少的那条路径
        //如果既要带出,又要带回,按带出计算
        tempPath.emplace_back(s);
        if(total_out < take_out)
        {
            take_out = total_out;
            bring_in = total_in;
            BestPath = tempPath;
        }
        else if(total_out == take_out)
        {
            if(total_in < bring_in)
            {
                bring_in = total_in;
                BestPath = tempPath;
            }
        }
        tempPath.pop_back();
        return;
    }
    tempPath.emplace_back(v);
    for(int i=0;i<pre[v].size();i++)
    {
        DFS(pre[v][i],s,cap);
    }
    tempPath.pop_back();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值