1018 Public Bike Management (30分)

34 篇文章 0 订阅

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 S
​3
​​ , we have 2 different shortest paths:

PBMC -> S
​1
​​ -> S
​3
​​ . In this case, 4 bikes must be sent from PBMC, because we can collect 1 bike from S
​1
​​ and then take 5 bikes to S
​3
​​ , so that both stations will be in perfect conditions.

PBMC -> S
​2
​​ -> S
​3
​​ . This path requires the same time as path 1, but only 3 bikes sent from PBMC and hence is the one that will be chosen.

Input Specification:
Each input file contains one test case. For each case, the first line contains 4 numbers: C
​max
​​ (≤100), always an even number, is the maximum capacity of each station; N (≤500), the total number of stations; S
​p
​​ , the index of the problem station (the stations are numbered from 1 to N, and PBMC is represented by the vertex 0); and M, the number of roads. The second line contains N non-negative numbers C
​i
​​ (i=1,⋯,N) where each C
​i
​​ is the current number of bikes at S
​i
​​ respectively. Then M lines follow, each contains 3 numbers: S
​i
​​ , S
​j
​​ , and T
​ij
​​ which describe the time T
​ij
​​ taken to move betwen stations S
​i
​​ and S
​j
​​ . All the numbers in a line are separated by a space.

Output Specification:
For each test case, print your results in one line. First output the number of bikes that PBMC must send. Then after one space, output the path in the format: 0−>S
​1
​​ −>⋯−>S
​p
​​ . Finally after another space, output the number of bikes that we must take back to PBMC after the condition of S
​p
​​ is adjusted to perfect.

Note that if such a path is not unique, output the one that requires minimum number of bikes that we must take back to PBMC. The judge’s data guarantee that such a path is unique.

Sample Input:
10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1
Sample Output:
3 0->2->3 0

非常好的题目,dijkstra+dfs,两个都必须好好掌握并且能灵活改变
因为这道题是不超过500个顶点,所以用邻接矩阵(小于1000都用邻接矩阵)。这道题我们要做的事有两件,一件是通过dijkstra来找出0点到问题点的最短路径,并记录到达每个点的前驱结点,这样就不用存一整条路径,而是通过最后一个结点反向走回0号点。第二件事是通过dfs前驱结点,来找出最短路径中最优的那条。

vector<int> pre[MAXV];//pre[i]存放的是i的前驱结点们

1.这里的dij和普通的dij不同的是,如果通过u到v可以优化d[v],那么就pre[v].clear(),清掉v的所有前驱结点们,并把upush_back,最后优化路径。如果相等,那么就把u给push_back()。
2…定义一个tempPath数组和path数组分别用于存放临时路径和最优路径,dfs是递归,所以有递归边界和递归式,递归边界:如果v==0,证明一条最短路径到达终点了,可以来进行筛选。定义一个need和remain,分别用于存放来的时候需要的数量和回去的时候放回去的数量。遍历这条路上的所有点,反向遍历这个tempPath数组,就可以从0走到终点了。如果weight[tempPath[i]]大于0证明要带走一部分车,如果<0证明需要车(然后再进行判断,手上的车是否足够还,remain是要还的车)。
遍历完这条路后,把当前need和remain和minNeed和minRemain比较。如果need<minNeed,就更新minNeed和minRemain,并且path = tempPathvector可以直接用等号赋值。如果need相等,remain比较小,就更新remain和path。最后tempPath.pop_back(),因为//只要弹出最后一个点就行了,举个栗子:因为有可能是3->0,还有3->1->0,所以继续递归3的前驱结点,然后return。
递归式:遍历当前结点的所有前驱,并dfs它,遍历完当前结点的所有子结点后,tempPath.pop_back()当前结点

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXV = 510;
const int INF = 1000000000;
//d为源点到各个点的最短距离,G为图
int d[MAXV], G[MAXV][MAXV];
int c, n, s, m;//s为有问题的那个点
int weight[MAXV];
bool vis[MAXV]={false};
int minNeed = INF, minRemain = INF;//需要带去的数量和需要带回的数量
vector<int> pre[MAXV];//前驱结点,per[i]是一个vector,存放i的前驱结点们
vector<int> tempPath, path;//临时路径和最优路径

void Dijkstra(int st){
    fill(d, d+MAXV,INF);
    d[0] = 0;
    for(int i = 0; i <= n; i++){
        int u = -1, MIN = INF;//记录离当前结点最近的结点序号和距离
        for(int j = 0; j <= n; j++){
            if(vis[j] == false && d[j] < MIN){
                u = j;
                MIN = d[j];
            }
        }
        if(u == -1) return;//如果u还是为-1,证明已经没有连通块了
        vis[u] = true;
        //接下来是优化操作,遍历u的邻接节点,看看是否能通过u缩小源点到u邻接结点的距离
        for(int v = 0; v <= n; v++){
            if(G[u][v] != INF && vis[v] == false){
                if(d[u] + G[u][v] < d[v]){
                    pre[v].clear();
                    pre[v].push_back(u);
                    d[v] = d[u] + G[u][v];
                }
                else if(d[u] + G[u][v] == d[v]){
                    pre[v].push_back(u);
                }
            }
        }
    }
}


void DFS(int v){
    tempPath.push_back(v);
    //因为记录的是前驱结点,所以通过前驱数组只能从后往前走,是从s顶点也就是有问题的结点回0结点的
    //所以递归边界是v==0,遍历到v==0时,证明已经遍历完其中一条最短路了,这时就要和别的最短路比比看谁更优秀
    if(v == 0){
        int need = 0, remain = 0;
        for(int i = tempPath.size()-1; i>=0; i--){
            int id = tempPath[i];
            if(weight[id] > 0){//证明这个点需要带走一部分车,带车比给车容易,给车还要考虑手上的车是否足够
                remain += weight[id];
            }
            else{
                if(remain > abs(weight[id])){//手上的车足够还
                    remain -= abs(weight[id]);
                }
                else{
                    need += abs(weight[id]) - remain;
                    remain = 0;
                }
            }
        }
        //和其他最短路径的need还有remain比,如果更优秀就将tempPath变为path
        if(need < minNeed){
            minNeed = need;
            minRemain = remain;
            path = tempPath;
        }
        else if(need == minNeed && remain < minRemain){
            minRemain = remain;
            path = tempPath;
        }
        tempPath.pop_back();//只要弹出最后一个点就行了,举个栗子:因为有可能是3->0,还有3->1->0,所以继续递归3的前驱结点
        return;
    }
    for(int i = 0 ; i < pre[v].size(); i++){
        DFS(pre[v][i]);
    }
    //顶点在dfs访问后弹出
    tempPath.pop_back();
}

int main(){
    fill(G[0],G[0] + MAXV * MAXV, INF);
    cin>> c>> n>> s>> m;
    for(int i = 1; i <= n; i++){
        cin>>weight[i];
        weight[i] -= c/2;//每个点点权-最大值的一半,就知道这个点需要几辆车or多出几辆车
    }
    int u, v;
    for(int i = 0; i < m; i++){
        cin>> u>> v;
        cin>>G[u][v];
        G[v][u] = G[u][v];
    }
    Dijkstra(0);
    DFS(s);
    cout<<minNeed<<" ";
    for(int i = path.size() - 1; i >= 0; i--){
        cout<<path[i];
        if(i!=0) cout<<"->";
    }
    cout<<" "<<minRemain;
    return 0;
}

这道题挺厉害的,难的是dfs里的内容,反向遍历,记得不要把0号结点遍历进去了,因为0号的自行车数量是0,从0的下一个一路遍历到终点,两个情况,一个是该点有的多,就将remain加上多的;另一种是该点还缺,再分情况,我们手上的够还吗,够的话就将remain-少的,不够的话就相当于要从中心带出来,need加上缺的-remain,并将remain=0。然后就是看看能否优化

//先用dij求出pbmc到问题点的最短路径们
//用dfs来判断中间的点各有几个,算整条路上每个都是一半的话,要带几辆,或者要带回几辆
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 505;
const int INF =505;
int G[maxn][maxn];
int dis[maxn];
bool vis[maxn] = {false};
int weight[maxn] = {0};

vector<int> preNode[maxn];
vector<int> tempPath;
vector<int> path;
int capacity, n, e,m;//e是problem station
int minSend = INF,minCollect = INF;

void dijkstra(int s){
    fill(dis,dis+maxn,INF);
    dis[s] = 0;
    for(int i = 0; i <= n; i++){
        int u = -1, minDis = INF;
        for(int j = 0; j <= n; j++){
            if(vis[j] == false && dis[j] < minDis){
                u = j;
                minDis = dis[j];
            }
        }
        if(u == -1) return;
        vis[u] = true;
        for(int v = 0; v <= n; v++){
            if(vis[v] == false && G[u][v] != INF){
                if(G[u][v]+dis[u] < dis[v]){
                    dis[v] = G[u][v] + dis[u];
                    preNode[v].clear();
                    preNode[v].push_back(u);
                }
                else if(G[u][v] + dis[u] == dis[v]){
                    preNode[v].push_back(u);
                }
            }
        }
    }
}

void dfs(int v){//此时已经有最短路径了,就是比谁要带的车少就行了,但是要记录带来几辆,带回几辆
    tempPath.push_back(v);
    if(v == 0){
        int need=0,remain=0;
        for(int i = tempPath.size()-2; i >= 0; i--){//从起点走到终点,记录一路上的车是否要给,或者是否够
            if(weight[tempPath[i]] > capacity/2){//如果有的多
                remain += (weight[tempPath[i]] - capacity/2);
            }
            else if(weight[tempPath[i]] < capacity/2){//如果这个站的车不够,分情况讨论
                if(remain > (capacity/2 - weight[tempPath[i]])){//手上的车够给
                    remain -= (capacity/2 - weight[tempPath[i]]);
                }
                else if(remain < (capacity/2 - weight[tempPath[i]])){//手上的车不够给,这时候就需要考虑从pbmc带车过来了
                    need += (capacity/2 - weight[tempPath[i]] -remain);
                    remain = 0;
                }
            }
        }
        if(need < minSend){
            path = tempPath;
            minSend = need;
            minCollect = remain;
        }
        else if(need == minSend && remain < minCollect){
            path = tempPath;
            minCollect = remain;
        }
        tempPath.pop_back();
        return;
    }
    for(int i = 0; i < preNode[v].size(); i++){
        dfs(preNode[v][i]);
    }
    tempPath.pop_back();
}

int main(){
    cin>>capacity>>n>>e>>m;
    fill(G[0],G[0]+maxn*maxn,INF);
    for(int i = 1; i <= n; i++){
        cin>>weight[i];//记录从1号结点的开始的每个结点的自行车数量
    }
    int u,v,dist;
    for(int i = 0; i < m; i++){
        cin>>u>>v>>dist;
        G[u][v] = G[v][u] = dist;
    }
    dijkstra(0);
    dfs(e);
    reverse(path.begin(),path.end());
    cout<<minSend<<" ";
    for(int i = 0; i < path.size(); i++){
        if(i!=0) cout<<"->";
        cout<<path[i];
    }
    cout<<" "<<minCollect;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值