PAT (Advanced) 1018. Public Bike Management (30)

本文介绍了一个基于Dijkstra算法和深度优先搜索(DFS)解决公共自行车调度问题的方法。通过对所有等距离路径的遍历,找到最优的自行车调度方案,即所需携带和调整的自行车数量最少的路径。

原题:1018. Public Bike Management (30)




解题思路:

1.题目是最小路径问题,采用Dijkstra算法。

2.题目中路径的判优条件十分复杂,于是改变思路,先求出所有距离相同的路径,再通过DFS算法遍历所有可能路径,求出最优路径。

DFS中判优流程如下:

将车站分为三类:(1)车子多于当前要求,将多于的车子加入remain;(2)车子少于当前要求,并且少的数量小于等于remain的数量,则在remain中减去少的数量;(3)车子少于当前的要求,且大于remain中的数量,将remain置零,并将不够的量加入take。

注:remain为模拟该路径时到达某一车站保有的车辆,take为需要从总站带的车辆。



C++代码如下:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 0x3fffffff;
int G[maxn][maxn];
int vis[maxn];
int bikeNum[maxn];
int d[maxn];
vector<int> pre[maxn]; //pre[i]存储i点每个可能的前驱
int n, m, c, des;
void Dijkstra(int st)
{
    fill(d, d + maxn, INF);
    fill(vis, vis + maxn, 0);

    d[st] = 0;
    for(int i = 0; i <= n; i++)
    {
        int u = -1, MIN = INF;
        for(int j = 0; j <= n; j++)
        {
            if(vis[j] == 0 && d[j] < MIN)
            {
                u = j;
                MIN = d[j];
            }
        }

        if(u == -1) return ;
        vis[u] = 1;
        for(int j = 0; j <= n; j++)
        {
            if(G[u][j] != INF && vis[j] == 0)
            {
                if(d[j] > d[u] + G[u][j])
                {
                    d[j] = d[u] + G[u][j];
                    pre[j].clear();
                    pre[j].push_back(u);
                }
                else if(d[j] == d[u] + G[u][j])
                {
                    //路径长度相等时,也存入可能路径
                    pre[j].push_back(u);
                }
            }
        }
    }
}
int minRemain, minTake; //保存最小携带车辆,最小带回车辆
vector<int> ans, temp; //保存最终结果和临时结果
//遍历所有路径
void DFS(int des)
{
    if(des == 0)
    {
        //当遍历到0时, 说明temp中保存了一条0到目的地的路径
        //对该路径进行判优
        temp.push_back(des);
        int remain = 0, take = 0;
        for(int i = temp.size() - 2; i >= 0; i--)
        {
            //要从0开始模拟
            int id = temp[i];
            //当当前车站的车子多于要求的数量时
            if(bikeNum[id] >= c / 2)
            {
                remain += bikeNum[id] - c / 2;
            }
            else
            {//少于时
                if(remain > c / 2 - bikeNum[id])
                {
                    //走到当前节点时携带的车辆比需求的多时
                    remain -= c / 2 - bikeNum[id];
                }
                else
                {
                    //走到当前节点时携带的车辆比需求的少时
                    take += ((c / 2 - bikeNum[id]) - remain);
                    remain = 0;
                }
            }
        }
        if(take < minTake)
        {
            //需要携带的车辆更少
            minTake = take;
            minRemain = remain;
            ans = temp;
        }
        else if(take == minTake && remain < minRemain)
        {
            //需要带回的车辆更少
            minRemain = remain;
            ans = temp;
        }

        temp.pop_back(); //弹出末尾节点再返回
        return ;
    }
    temp.push_back(des); //加入当前节点
    for(int i = 0; i < pre[des].size(); i++)
    {
        //遍历当前节点的可能前驱
        DFS(pre[des][i]);
    }
    //弹出当前节点
    temp.pop_back();
}

int main()
{
    while(scanf("%d%d%d%d", &c, &n, &des, &m) != EOF)
    {
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= n; j++)
                G[i][j] = INF;
        //bikeNum[0] = 0;
        for(int i = 1; i <= n; i++)
            scanf("%d", &bikeNum[i]);
        for(int i = 0; i < m; i++)
        {
            int a, b, dis;
            scanf("%d%d%d", &a, &b, &dis);
            G[a][b] = dis;
            G[b][a] = dis;
        }
        Dijkstra(0);
        int v = des;
        minRemain = INF;
        minTake = INF;
        DFS(des);
        printf("%d ", minTake);
        for(int i = ans.size() - 1 ; i >= 0; i--)
        {
            if(i == ans.size() - 1)
                printf("%d", ans[i]);
            else
                printf("->%d", ans[i]);
        }
        printf(" %d\n", minRemain);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值