PAT(甲级)渡劫(一)-Public Bike Management

PAT(甲级)渡劫(一)-Public Bike Management

题目描述:

There is a public bike service in Hangzhou City which provides great convience to the tourists from all over the world. One may rent a bike at any station and return it to any other station in the city.
The Public Bike Management Center(PBMC) Keeps monitoring the real-time capacity of all the station. 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.

Figure 1 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:

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

2. PBMC->S2->S3. 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

输入例子:
10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1
输出例子
3 0->2->3 0
算法分析:

​ 这道题用dijkstra过不了。**如果只有Dijkstra是不可以的,因为minTake和minBring在路径上的传递不满足最优子结构,不是简单的相加的过程,只有在所有路径都确定了之后才能去选择最小的need和最小的back。**所以我们可以用dfs搜索所有最短路,找出最优解

代码如下:
#include <iostream>
#include <cstdio>
#include <vector>
#define INF 99999999
using namespace std;

struct station{ // 自行车站结构体
    int time,bike;  // 到达该站的时间以及该站的自行车数量
    station(){}
    station(int bike):bike(bike){   // 初始化到达该站的时间为无穷,表示不可达
        time = INF;
    }
};

struct edge{
    int next;   // 指向下一个结点的编号
    int length;
    edge(int a,int b):next(a),length(b){}
};

vector<edge> G[510];    // 邻接链表
station st[510];        // 保存车站信息

bool visited[510] = {false};   // 访问标记数组

vector<int> path;   // 路径数组
vector<int> anspath;    // 保存最后的路线数组

// 初始化
// 大写表示最终结果,小写表示操作变量,
int Sum = INF,sum = 0;      // 记录花费的时间
int Bring = INF,bring = 0;  // 记录需要从PBMC携带的自行车数量
int Take = INF,take = 0;    // 记录我们需要带回PBMC自行车的数量

int Cmax,N,Sp,M;    // Cmax: 一个站的最大容量
                    // N: 站的数量
                    // Sp: 目的站的编号
                    // M: 路的数量

void dfs(int a){
    if(sum > Sum) return ;
    if(a == Sp){    // 当为终点时,判断是否为最优解
        if(Sum > sum || (Sum == sum && (Bring > bring || (Bring == bring && Take > take)))){
            Sum = sum;
            Bring = bring;
            Take = take;
            anspath = path;
        }
        return ;
    }
    for(int i = 0 ; i < G[a].size(); i++){
        int next = G[a][i].next,length = G[a][i].length;
        int tempbring = bring,temptake = take,tempsum = sum;    // 保存值,便于回溯
        if(visited[next] == true) continue;
        if(st[next].time >= st[a].time + length){// 若下一点可达
            visited[next] = true;
            take += st[next].bike;
            if(take < 0){
                bring -= take;  // 若take为负,表示需要带回
                take = 0;
            }
            sum += length;  // 累加所需经过的时间
            st[next].time = st[a].time + length;
            path.push_back(next);  // 将该结点加入路径
            dfs(next);  // 继续深度遍历该结点

            visited[next] = false;  // 回溯
            bring = tempbring;
            take = temptake;
            sum = tempsum;
            path.pop_back();
        }
    }
}

int main(){
    scanf("%d%d%d%d",&Cmax,&N,&Sp,&M);
    Cmax /= 2; // 最佳存储量为最大容量的一半
    int bike;
    // 初始化起点0,设置时间与bike数量为0,时间为0可达
    st[0] = station(0);
    st[0].time = 0;
    for(int i = 1 ; i <= N ; i++){
        // 输入每个站的自行车存储量
        scanf("%d",&bike);
        st[i] = station(bike-Cmax); // bike为正则需带回,为负则需调配
    }

    int a,b,c;
    for(int i = 0 ; i < M ; i++){
        // 输入每条路的信息
        scanf("%d%d%d",&a,&b,&c);
        G[a].push_back(edge(b,c));
        G[b].push_back(edge(a,c));
    }
    dfs(0);
    printf("%d 0",Bring);
    for(int i = 0 ; i < anspath.size() ; i++){
        printf("->%d",anspath[i]);
    }
    printf(" %d",Take);

    return 0;
}
运行结果:

其他答案:
#include <iostream>
#include <cstdio>
#include <vector>

using namespace std;

const int inf = 1e9;
int perfect,n,sp,m;
int c[505];                 // 记录车站现存的自行车,下标代表车站编号
int g[505][505] = {0};      // 邻接矩阵
bool visited[505] = {true}; // 访问标记数组,初始化为false
vector<int> tPath,path;     // path存储最佳路径,tpath为临时变量
int len = 0,minLen = inf;
int need = 0,back = 0;      // need表示需要带去的自行车,back表示需要带回的自行车
int minNeed = inf,minBack = inf;    // 记录最少的need和back

void dfs(int s){
    if(s == sp){// 如果是终点,若有多条,判断是否为最优解
        if(len < minLen){
            path = tPath;
            minLen = len;
            minNeed = need;
            minBack = back;
        }else if(need < minNeed || (need == minNeed && back < minBack)){
            path = tPath;
            minNeed = need;
            minBack = back;
        }
        return;
    }

    for(int i = 1 ; i <= n ; i++){
        if(!visited[i] && g[s][i]){ // 访问s结点的邻接结点
            int tn = need,tb = back;    // 暂存此时的need,back,因为后面需要修改,便于后面回溯恢复到原来的状态
            if(c[i] < perfect){
                if(back >= perfect - c[i]) back -= perfect - c[i];
                else{
                    need += perfect - c[i] - back;
                    back = 0;
                }
            }else{
                back += c[i]-perfect;
            }
            visited[i] = true;
            len += g[s][i];
            tPath.push_back(i);
            if(len <= minLen) dfs(i);
            // 回溯
            need = tn,back = tb;
            visited[i] = false;
            len -= g[s][i];
            tPath.pop_back();
        }
    }
}

int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d%d%d%d",&perfect,&n,&sp,&m);
    perfect /= 2;   // 最大容量的一半表示最佳容量
    for(int i = 1 ; i <= n ; i++)   // 输出每个车站的自行车的数量
        scanf("%d",c+i);
    
    for(int i = 0 ; i < m ; i++){
        int s1,s2,e;
        scanf("%d%d%d",&s1,&s2,&e);
        g[s1][s2] = g[s2][s1] = e;
    }
    dfs(0);
    printf("%d 0",minNeed); // 输出最少需要带去的车辆数目
    for(int i = 0 ; i < path.size() ; i++)  // 输出最佳路径
        printf("->%d",path[i]);
    printf(" %d",minBack);  // 输出最少需要带回的车辆数目
    return 0;
}
运行结果:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值