[PAT解题报告] Public Bike Management
给定一个无向图,一个点表示有无穷多自行车的车管所,每个点有一定的车,每个点的容量都相同,如果有的点车辆少于容量的一半的话,旧需要从车管所运送自行车过去,顺便把经过点多余的车运回去。给定缺车的节点,需要考虑几个指标:
(1)最短路
(2)需要的运过去车最少
(3)需要运回来的车最少
分析 :
图的节点不多,可以直接用临接矩阵。求最短路可以同时记录路径,关键是最短路有多条,每条都得记录,我用的vector数组表示每个点的前一个节点的可能性——求出最短路在枚举所有最短路,这个就dfs了,好在数据不大,暴力枚举能过。我求最短路是正着求的,其实可以只求到目的地就行。然后枚举的时候枚举全部路径,这样不太好减枝,因为带入的车数mayin可能会变小……别忘记最后路径要反向,因为是不断往起点找的。
代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
const int inf = 1000000000;
const int N = 502;
int n;
bool mark[N];
int a[N][N];
int b[N];
int cost[N];
vector<int> pre[N];
vector<int> answer;
int bestin, bestout, m;
void dijkstra() {
for (int i = 0; i < n; ++i) {
cost[i] = inf;
mark[i] = false;
}
cost[0] = 0;
for (int j = 0; j < n; ++j) {
int k = -1;
for (int i = 0; i < n; ++i) {
if ((!mark[i]) && ((k < 0) || (cost[i] < cost[k]))) {
k = i;
}
}
mark[k] = true;
for (int i = 0; i < n; ++i) {
if ((!mark[i]) && (a[k][i] < inf)) {
int temp = cost[k] + a[k][i];
if (temp < cost[i]) {
pre[i].resize(1);
pre[i][0] = k;
cost[i] = temp;
}
else if (temp == cost[i]) {
pre[i].push_back(k);
}
}
}
}
}
void dfs(int now, int mayin, int mayout, vector<int> &path) {
path.push_back(now);
if (now == 0) {
if ((mayin < bestin) || ((mayin == bestin) && (mayout < bestout))) {
bestin = mayin;
bestout = mayout;
answer = path;
}
path.pop_back();
return;
}
if (b[now] >= m) {
mayin -= (b[now] - m);
if (mayin < 0) {
mayout -= mayin;
mayin = 0;
}
}
else {
mayin += m - b[now];
}
for (int i = 0; i < pre[now].size(); ++i) {
dfs(pre[now][i], mayin, mayout, path);
}
path.pop_back();
}
int main() {
int p, to;
scanf("%d%d%d%d",&m,&n,&to,&p);
for (int i = 1; i <= n; ++i) {
scanf("%d",b + i);
}
++n;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
a[i][j] = inf;
}
}
for (;p;--p) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[x][y] = a[y][x] = min(a[x][y],z);
}
m >>= 1;
dijkstra();
bestin = bestout = inf;
vector<int> path;
dfs(to, 0, 0, path);
reverse(answer.begin(), answer.end());
printf("%d ",bestin);
for (int i = 0; i < answer.size(); ++i) {
if (i) {
printf("->");
}
printf("%d", answer[i]);
}
printf(" %d\n",bestout);
return 0;
}
另外一种方法也差不多,就是计算最短路的时候从终点往起点做。这样dfs的时候路径是正向的,还有一个好处是,这样带入车辆的数mayin是不断增加的,所以比较方便剪枝。
代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
const int inf = 1000000000;
const int N = 502;
int n;
bool mark[N];
int a[N][N];
int b[N];
int cost[N];
vector<int> Next[N];
vector<int> answer;
int to, bestin, bestout, m;
void dijkstra() {
for (int i = 0; i < n; ++i) {
cost[i] = inf;
mark[i] = false;
}
cost[to] = 0;
for (int j = 0; j < n; ++j) {
int k = -1;
for (int i = 0; i < n; ++i) {
if ((!mark[i]) && ((k < 0) || (cost[i] < cost[k]))) {
k = i;
}
}
mark[k] = true;
for (int i = 0; i < n; ++i) {
if ((!mark[i]) && (a[k][i] < inf)) {
int temp = cost[k] + a[k][i];
if (temp < cost[i]) {
Next[i].resize(1);
Next[i][0] = k;
cost[i] = temp;
}
else if (temp == cost[i]) {
Next[i].push_back(k);
}
}
}
}
}
void dfs(int now, int mayin, int mayout, vector<int> &path) {
if (mayin > bestin) {
return;
}
path.push_back(now);
if (b[now] >= m) {
mayout += b[now] - m;
}
else {
mayout -= m - b[now];
if (mayout < 0) {
mayin -= mayout;
mayout = 0;
}
}
if (now == to) {
if ((mayin < bestin) || ((mayin == bestin) && (mayout < bestout))) {
bestin = mayin;
bestout = mayout;
answer = path;
}
path.pop_back();
return;
}
for (int i = 0; i < Next[now].size(); ++i) {
dfs(Next[now][i], mayin, mayout, path);
}
path.pop_back();
}
int main() {
int p;
scanf("%d%d%d%d",&m,&n,&to,&p);
for (int i = 1; i <= n; ++i) {
scanf("%d",b + i);
}
++n;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
a[i][j] = inf;
}
}
for (;p;--p) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[x][y] = a[y][x] = min(a[x][y],z);
}
b[0] = (m >>= 1);
dijkstra();
bestin = bestout = inf;
vector<int> path;
dfs(0, 0, 0, path);
printf("%d ",bestin);
for (int i = 0; i < answer.size(); ++i) {
if (i) {
printf("->");
}
printf("%d", answer[i]);
}
printf(" %d\n",bestout);
return 0;
}
原题链接: http://www.patest.cn/contests/pat-a-practise/1018
查看10道真题和解析