PAT甲级(Dijkstra结合模拟)--Public Bike Management

Posted C_YCBX Py_YYDS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PAT甲级(Dijkstra结合模拟)--Public Bike Management相关的知识,希望对你有一定的参考价值。


我的个人小站

题目


OJ平台

题目翻译

单词积累

is said to 据说
is said to be 被说成
#例句
 A station is said to be in perfect condition if it is exactly half-full.  
#翻译
如果一个站点确切的达到了一半满,则可以说它处于完美状态。
illustrates v.举例
vertices/vertex  顶点
correspond  通信、相当于、与...一致
#例句
The stations are represented by vertices and the roads correspond to the edges. 
#翻译
站点用顶点表示,且每个道路用边来建立联系。

关键句子分析

#原句
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.
#翻译
如果一个车站正好是半满的,就被称为状态完美。
如果一个站点是满的或空的,PBMC将收集或发送自行车来调整该站点来完善。
此外,沿途的所有车站也将进行调整。当到达问题站时,PBMC总是选择到达该站的最短路径。如果有多个最短路径,则选择需要从PBMC发送自行车数量最少的路径

输入数据描述

#原文
For each case, the first line contains 4 numbers: Cmax (<= 100),
always an even number, is the maximum capacity of each station; N (<= 500), the total number of stations;
Sp, 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 Ci (i=1,…N) where each Ci is the current number of bikes at Si respectively.
Then M lines follow, each contains 3 numbers: Si, Sj, and Tij which describe the time Tij taken to move betwen stations Si and Sj.
All the numbers in a line are separated by a space.
#翻译
对于每种情况,第一行包含 4 个数字:
Cmax (<= 100),始终是偶数,是每个站的最大容量; N(<=500),总站数; sp,问题站点的索引(站点编号从1到N,PBMC用顶点0表示);
M,道路的数量。 第二行包含 N 个非负数 Ci (i=1,…N),其中每个 Ci 分别是当前在 Si 处的自行车数量。
然后是 M 行,每行包含 3 个数字:Si、Sj 和 Tij,它们描述了 Tij 在站点 Si 和 Sj 之间移动所花费的时间。
注意:一行中的所有数字都用空格分隔。

总结题目大意:
我们题目给出各个结点以及他们之间的边权值,我们需要让它们的点权值满足最大容量的一半,而这个前往目标地的过程中,如果大于一半我们就把多出来的回收,如果小于一半则发送自行车直到该站半满。
而我们的输出打印只有一行:第一个是需要从PBMC发送出的自行车数量,然后是路径打印,最后是需要回收的自行车数量。

代码详解

效率还行

本题我采用链式前向星存图,不会的完全可以用其他方式代替。

Input函数

// 输入处理,注意初始化数据,由于全局变量默认为0,还是需要初始化一些数据的
// 需要注意的是每个边是双向的,所以建图的时候也得双向建立
void Input() {
    ios::sync_with_stdio(false);
    memset(dist, 0x3f, sizeof(dist));
    memset(path, 0xff, sizeof(path));
    memset(head, 0xff, sizeof(head));
    cin >> c >> n >> sp >> m;
    for (int i = 1; i <= n; i++) {
        cin >> cap[i];
    }
    while (m--) {
        int a, b, len;
        cin >> a >> b >> len;
        add(a, b, len);
        add(b, a, len);
    }
}

getVal函数:模拟发送和回收的过程

// (不瞒xdm说,就是这个这个东西卡了我四个小时)根据题目的意思,需要返回发送值和回收值
// 由于当我们调用path进行寻路的时候,肯定是被倒着来的,所以如果要模拟全过程,可以通过栈来进行模拟。
// 当然也可以用函数递归的方式来进行处理
pair<int, int> getVal(int pre) {
    vector<int>St;
    St.push_back(sp);
    while (pre != 0) {
        St.push_back(pre);
        pre = path[pre];
    }
    int helper = 0;
    int send = 0;
    while (!St.empty()) {
        int pos = St.back(); St.pop_back();
        // 计算当前容量和half容量差多少,为负数则更新send,为非负数则更新helper
        int t = cap[pos] - (c / 2);
        if (t > 0) {
            helper += t;
        } else if (t < 0) {
            t = -t;
            if (helper < t) {send += t - helper; helper = 0;}
            else {helper -= t;}
        }
    }
    return {send, helper};
}

Dijkstra算法的处理

注意这个对多条最短路径求符合题意的值的处理方式和我之前做过的那道Dijkstra如出一辙(这里注意还需要在刚求出最短路径的那一刻更新数据),大家可以看看Emergency

// 以下皆为Dijkstra算法的基本套路,需要注意的是对多条最短路径的处理方式
int findMin() {
    int minV = INF;
    int minN = 0;
    for (int i = 1; i <= n; i++) {
        if (!visit[i] && dist[i] < minV) {
            minV = dist[i];
            minN = i;
        }
    }
    visit[minN] = true;
    //在此处提前先让sendNums得到一次赋值
    if (minN == sp) {
        auto cmp = getVal(path[sp]);
        if (cmp.first < sendNums) {
            sendNums = cmp.first;
            recall = cmp.second;
        }
    }
    return minN;
}

void Dijkstra() {
    for (int k = 0; k < n; k++) {
        int node = findMin();
        if (node == 0)
            break;
        for (int i = head[node]; i != -1; i = edge[i].next) {
            if (!visit[edge[i].to]) {
                if (dist[edge[i].to] > edge[i].len + dist[node]) {
                    dist[edge[i].to] = edge[i].len + dist[node];
                    path[edge[i].to] = node;
                    if (edge[i].to == sp) {
                        auto cmp = getVal(node);
                        sendNums = cmp.first;
                        recall = cmp.second;
                    }
                } else if (dist[edge[i].to] == edge[i].len + dist[node]) {
                    //如果有多条最短路径,则根据sendNums进行选择,选择途经bike最多的情况。
                    if (edge[i].to == sp) {
                        auto cmp = getVal(node);
                        if (cmp.first < sendNums) {
                            path[sp] = node;
                            sendNums = cmp.first;
                            recall = cmp.second;
                        }
                    }
                }
            }
        }
    }
}

打印函数处理print

// 打印结果部分
void print_t(vector<int>&t) {
    reverse(t.begin(), t.end());
    cout << 0;
    for (int i = 0; i < t.size(); i++) {
        cout << "->" << t[i];
    }
}

void print() {
    vector<int>temp; temp.push_back(sp);
    cout << sendNums << ' ';
    for (int i = path[sp]; i != 0; i = path[i]) {
        temp.push_back(i);
    }
    print_t(temp);
    cout << ' ' << recall;
}

整合代码得到答案

#include<bits/stdc++.h>
#define N 505
using namespace std;
// INF定义为理论上的最大值
const int INF = 0x3f3f3f3f;
// c是每个地方允许的最大容量,n是地方的编号,sp为需要到达的地区,m为边的个数
int c, n, sp, m;

//题目所需数据:
// sendNums表示PBMC需要发送的公共单车个数
// recall表示需要发回的公共单车的个数
// cap[]表示第i个地点的当前容量
int sendNums = INF;
int recall;
int cap[N];


//Dijkstra算法需要的基本参数
int path[N];
int dist[N];
bool visit[N];


//链式前向星建图
int head[N];
int tot = 0;
struct {
    int to;
    int len;
    int next;
} edge[N * N];
void add(int a, int b, int len) {
    edge[tot].to = b;
    edge[tot].len = len;
    edge[tot].next = head[a];
    head[a] = tot++;
}

// 输入处理,注意初始化数据,由于全局变量默认为0,还是需要初始化一些数据的
// 需要注意的是每个边是双向的,所以建图的时候也得双向建立
void Input() {
    ios::sync_with_stdio(false);
    memset(dist, 0x3f, sizeof(dist));
    memset(path, 0xff, sizeof(path));
    memset(head, 0xff, sizeof(head));
    cin >> c >> n >> sp >> m;
    for (int i = 1; i <= n; i++) {
        cin >> cap[i];
    }
    while (m--) {
        int a, b, len;
        cin >> a >> b >> len;
        add(a, b, len);
        add(b, a, len);
    }
}

// (不瞒xdm说,就是这个这个东西卡了我四个小时)根据题目的意思,需要返回发送值和回收值
// 由于当我们调用path进行寻路的时候,肯定是被倒着来的,所以如果要模拟全过程,可以通过栈来进行模拟。
// 当然也可以用函数递归的方式来进行处理
pair<int, int> getVal(int pre) {
    vector<int>St;
    St.push_back(sp);
    while (pre != 0) {
        St.push_back(pre);
        pre = path[pre];
    }
    int helper = 0;
    int send = 0;
    while (!St.empty()) {
        int pos = St.back(); St.pop_back();
        // 计算当前容量和half容量差多少,为负数则更新send,为非负数则更新helper
        int t = cap[pos] - (c / 2);
        if (t > 0) {
            helper += t;
        } else if (t < 0) {
            t = -t;
            if (helper < t) {send += t - helper; helper = 0;}
            else {helper -= t;}
        }
    }
    return {send, helper};
}


// 以下皆为Dijkstra算法的基本套路,需要注意的是对多条最短路径的处理方式
int findMin() {
    int minV = INF;
    int minN = 0;
    for (int i = 1; i <= n; i++) {
        if (!visit[i] && dist[i] < minV) {
            minV = dist[i];
            minN = i;
        }
    }
    visit[minN] = true;
    //在此处提前先让sendNums得到一次赋值
    if (minN == sp) {
        auto cmp = getVal(path[sp]);
        if (cmp.first < sendNums) {
            sendNums = cmp.first;
            recall = cmp.second;
        }
    }
    return minN;
}

void Dijkstra() {
    for (int k = 0; k < n; k++) {
        int node = findMin();
        if (node == 0)
            break;
        for (int i = head[node]; i != -1; i = edge[i].next) {
            if (!visit[edge[i].to]) {
                if (dist[edge[i].to] > edge[i].len + dist[node]) {
                    dist[edge[i].to] = edge[i].len + dist[node];
                    path[edge[i].to] = node;
                    if (edge[i].to == sp) {
                        auto cmp = getVal(node);
                        sendNums = cmp.first;
                        recall = cmp.second;
                    }
                } else if (dist[edge[i].to] == edge[i].len + dist[node]) {
                    //如果有多条最短路径,则根据sendNums进行选择,选择途经bike最多的情况。
                    if (edge[i].to == sp) {
                        auto cmp = getVal(node);
                        if (cmp.first < sendNums) {
                            path[sp] = node;
                            sendNums = cmp.first;
                            recall = cmp.second;
                        }
                    }
                }
            }
        }
    }
}


// 打印结果部分
void print_t(vector<int>&t) {
    reverse(t.begin(), t.end());
    cout << 0;
    for (int i = 0; i < t.size(); i++) {
        cout << "->" << t[i];
    }
}

void print() {
    vector<int>temp; temp.push_back(sp);
    cout << sendNums << ' ';
    for (int i = path[sp]; i != 0; i = path[i]) {
        temp.push_back(i);
    }
    print_t(temp);
    cout << ' ' << recall;
}

int main() {
    Input();
    // 第一次Dijkstra算法的初始化
    dist[0] = 0;
    visit[0] = true;
    for (int i = head[0]; i != -1; i = edge[i].next) {
        dist[edge[i].to] = edge[i].len;
        path[edge[i].to] = 0;
    }
    Dijkstra();
    print();
    return 0;
}

以上是关于PAT甲级(Dijkstra结合模拟)--Public Bike Management的主要内容,如果未能解决你的问题,请参考以下文章

PAT甲题题解-1111. Online Map (30)-PAT甲级真题(模板题,两次Dijkstra,同时记下最短路径)

PAT甲级 All Roads Lead to Rome (dijkstra+dfs回溯)

PAT-甲级-1017. Queueing at Bank模拟

PAT(甲级)2020年春季考试 7-4 Replacement Selection

PAT甲级 Are They Equal (25) (恶心模拟)

PAT 甲级 1105 Spiral Matrix (25分)(螺旋矩阵,简单模拟)