PAT-1018 Public Bike Management(dijkstra + dfs)

1018. Public Bike Management 


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.


Figure 1

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.

Input Specification:

Each input file contains one test case. 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.

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->S1->...->Sp. Finally after another space, output the number of bikes that we must take back to PBMC after the condition of Sp 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


题目大意:每个自行车站容量为cmax,当放置车数为cmax/2时则为完美状态。现在给出cmax,车站数n,出问题的车站sp,m条互相可达的边以及它们的距离,从起点到达sp的路上会沿途调整那些需要调整的车站。此时需要求出:从出发点到问题车站所需距离最短的路线;如果有多条距离相同的路,则选择需要从起点携带车数最少的路;如果还是不唯一,则选择带回车数最少的路线。


主要思想:1. 此题是加权有向图的最短路径问题,很容易想到dijkstra,但是dijkstra无法解决第三点要求,即距离相同、带出车数相同时带回车数最小,带回车数需要在整条路径经过之后才能判断,无法在过程中立即做出选择。

所以需要使用dijkstra+dfs的方法,先找到其中一条最短路径,此时每个点到起点的最短距离都已经获得,然后利用深度优先搜索从终点反向搜索来找出所有存在的最短路径,每一次与当前保存的路径比较,从而找出其中符合要求的最优的那一条。注意反向搜索时只访问那些在其中一条最短路径上的顶点。

//dijkstra + dfs
#include <stdio.h>
#include <limits.h>
#define MAXN 501
 
int num[MAXN];						//各点的车数
int s[MAXN][MAXN];					//邻接矩阵表示图
int marked[MAXN];					//标记已被放松过的顶点
int distTo[MAXN];					//各点相对于起点的距离
int next[MAXN];						//下一个顶点
int path[MAXN];						//符合要求的最短路径
  
void relax(int v);
int min();
void dfs(int v);
void cal(int i);
 
int n, c, sp;
int ns, nb;
//带出车的最小辆数 和 带回车的最小辆数
int n_sent = INT_MAX, n_back = INT_MAX;
  
int main(void) {
    int m, i, v, w, time;
    int count = 0;
      
    scanf("%d%d%d%d", &c, &n, &sp, &m);
    for (i = 1; i <= n; i++) {
        scanf("%d", &num[i]);      
    }
    for (i = 0; i < m; i++) {
        scanf("%d%d%d", &v, &w, &time);
        s[v][w] = time;
        s[w][v] = time;
    }
	for (i = 1; i <= n; i++) 
		distTo[i] = INT_MAX;			//初始化各顶点到起点的距离
	distTo[0] = 0;
    while (!marked[sp]) {				//当sp已经被放松,则说明最短路径已找到
        relax(min());
    }
    dfs(sp);
 
    printf("%d ", n_sent);
    for (i = 0; path[i] != sp; i++) {
        printf("%d->", path[i]);
    }
    printf("%d ",  sp);
    printf("%d
", n_back);
   
    return 0;
}
  
void relax(int v) {
    int i, t;
      
    marked[v] = 1;
    for (i = 0; i <= n; i++) {
        t = s[v][i];
        if (t == 0 || marked[i])      continue;
        if (distTo[i] > distTo[v] + t) {
            distTo[i] = distTo[v] + t;                 
        }
    }
}
  
void dfs(int v) {
    int i, count, t;
     
    for (i = 0; i <= n; i++) {
        t = s[v][i];
		//只访问已被标记过的顶点而且 该点必须是在其中一条最短路径上
        if (t == 0 || !marked[i] || (distTo[i] + t != distTo[v]))	continue;
        next[i] = v;  
        dfs(i);
    }
    if (v != 0)     return;		
    
    ns = 0;				//需要从起点携带的车数
    nb = 0;				//当前身上的车数
    for (i = next[0]; i != sp; i = next[i]) {
        cal(i);
    }
    cal(sp);
	//当前这条最短路径与已经保存的最优解比较,判断是否需要更新最优路径
    if (ns < n_sent || (ns == n_sent && nb < n_back)) {
        count = 0;
        n_sent = ns;
        n_back = nb;
        for (i =  0; i != sp; i = next[i]) {
            path[count++] = i;
        }
        path[count] = sp;
    }
}

//计算到达每一个点时 要从起点携带的车数(是一直累加的) 和 当时身上的车数(要带回的车数)
void cal(int i) {
    int x;
 
    if (num[i] > c/2)					//此站车多,从中取出
        nb += num[i] - c/2;
    else if (num[i] < c/2) {			//此站车少,补充放入
        x = nb - (c/2 - num[i]);
        if (x >= 0)
            nb = x;
        else {							//身上的不够,起点携带数增加
            ns += -x;
            nb = 0;
        }
    }
}
 
//找到距离起点最短的点,即下一个需要放松的点
int min() {
    int i, min_index;
    int min = INT_MAX;
      
    for (i = 0; i <= n; i++) {
        if (!marked[i] && distTo[i] < min) {
            min = distTo[i];
            min_index = i;
        }
    }
      
    return min_index;
}


2. 还有一种方法是只用dfs完成,利用回溯使图中只存在一条路线,这种方法不拘束于只找最短路径,而是找出所有从起点到终点的路径,每一次都与当前保存的最优解进行比较,判断是否更新。

//dfs
#include <stdio.h>
#include <limits.h>
#define MAXN 501
int num[MAXN];        
int s[MAXN][MAXN];   
int marked[MAXN];
int next[MAXN];
int path[MAXN];
int n, c, sp;
int ns, nb, nt;
int n_sent = INT_MAX, n_back = INT_MAX, t_path = INT_MAX;
void cal(int i);
void dfs(int v);
 
int main(void) {
    int m, i, j, v, w, time;
    int count = 0;
      
    scanf("%d%d%d%d", &c, &n, &sp, &m);
    for (i = 1; i <= n; i++) {
        scanf("%d", &num[i]);      
    }
    for (i = 0; i < m; i++) {
        scanf("%d%d%d", &v, &w, &time);
        s[v][w] = time;
        s[w][v] = time;
    }   
    dfs(0);
    
    printf("%d ", n_sent);
    for (i = 0; path[i] != sp; i++) {
        printf("%d->", path[i]);
    }
    printf("%d ",  sp);
    printf("%d
", n_back);
   
    return 0;
}
 
void dfs(int v) {
    int i, t, count;
 
    marked[v] = 1;
    for (i = 0; i <= n; i++) {
        if (v == sp)    break;
        t = s[v][i];
        if (t > 0 && !marked[i]) {
            next[v] = i;
            dfs(i);
        }
    } 
    marked[v] = 0;                          //回溯,取消标记
    if (v != sp)    return;   
    nt = 0;
    ns = 0;
    nb = 0;
    for (i = 0; i != sp; i = next[i]) {
        nt += s[i][next[i]];
        if (i == 0) continue;
        cal(i);
    }
    cal(sp);
    if (nt > t_path)    return;
    if (nt == t_path) {
        if (ns < n_sent || (ns == n_sent && nb < n_back)) {
 
        }
        else return;
    }
    n_sent = ns;
    n_back = nb;
    t_path = nt;
    for (i = 0, count = 0; i != sp; i = next[i]) {
        path[count++] = i;
    }
    path[count] = sp;
}
 
void cal(int i) {
    int x;
 
    if (num[i] > c/2)
        nb += num[i] - c/2;
    else if (num[i] < c/2) {
        x = nb - (c/2 - num[i]);
        if (x >= 0)
            nb = x;
        else {
            ns += -x;
            nb = 0;
        }
    }
}



原文地址:https://www.cnblogs.com/zhayujie/p/7534857.html