问题描述
一组人要担任在一个特定城市举办的比赛的评委,他们需要找到最便宜的租车方式使得每个人都到达目标城市。他们观察发现,如果几个人在旅途的某一段坐同一辆租的车,就可以减少总费用。你的任务就是找出这些人应该采取的路线使得租车的总费用最小。
我们假定:
1. 租一辆车的费用与它行驶的距离成正比,没有燃油、保险、乘客人数多于一个等产生的额外费用。
2. 所有车的费用与行驶距离的比例相同。
3. 一辆车可以容纳任意数量的乘客。
4. 任意一对城市之间最多只有一条道路直接相连,每条道路都是双向的且长度大于0。
5. 每个人的起始城市到目标城市都至少有一种路线。
6. 若多个人的路线中经过同一城市,则这些人从该城市到目标城市必乘同一辆车。
7. 一个人可以乘一辆车到某个城市,再乘另一辆车离开该城市。
输入格式
第一行包含三个整数nc, dc和nr,表示地图上的城市个数,目标城市的编号和地图上的道路条数。
接下来nr行每行包含三个整数c1, c2和dist,表示一条长度为dist的双向道路(c1, c2)。
接下来一行包含一个整数nj,表示人数。
接下来一行包含nj个整数,表示每个人的起始城市。
输出格式
第一行包含“distance = ”和一个整数,表示所租的车行驶的最小总距离。
接下来nj行每行包含一个人的访问路线,城市按访问顺序给出并用“-”连接。
存在多种方案时,选择需要访问到的城市集合元素最少的一种;仍然存在多种方案时,选择集合元素升序排列后字典序最小的一种。
样例输入
5 3 5
1 2 1
2 3 2
3 4 3
4 5 1
2 4 2
2
5 1样例输出
distance = 6
5-4-2-3
1-2-3样例输入
4 4 3
1 3 1
2 3 2
3 4 2
2
1 2样例输出
distance = 5
1-3-4
2-3-4样例输入
3 3 3
1 2 2
1 3 3
2 3 1
2
2 1样例输出
distance = 3
2-3
1-2-3
数据规模和约定
对于30%的数据,1 <= nc <= 8。
对于100%的数据,1 <= nc <= 20,1 <= nj <= 10,1 <= dist <= 100。
解题思路
只能得90分,有两组数据超时。先二进制枚举选城市(第i位为1则在之后的最小生成树(可能会有两棵甚至多棵树)第i个城市会出现,反之)。然后对每个人用bfs判断是否可以到达目的城市。同时记录最优解(经过的距离最小的路线最优,距离相同的路线经过城市最少的最优)。最后对最优解用bfs对每个人找路,找到后输出。
代码
/*
输入格式
第一行包含三个整数nc, dc和nr,表示地图上的城市个数,目标城市的编号和地图上的道路条数。
接下来nr行每行包含三个整数c1, c2和dist,表示一条长度为dist的双向道路(c1, c2)。
接下来一行包含一个整数nj,表示人数。
接下来一行包含nj个整数,表示每个人的起始城市。*/
#include <iostream>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
struct Road {
int u,v,w,ord; //起点 终点 权值 编号
}R[10005];
struct Through {
int u,f,ord;
}T[10005];
int f[21];
int getf(int v)
{
if(f[v] == v)
return v;
f[v] = getf(f[v]);
return f[v];
}
bool Merge(int v1,int v2)
{
int f1 = getf(v1);
int f2 = getf(v2);
if(f1 != f2) {
f[f2] = f1;
return true;
}
return false;
}
bool cmp (Road r1,Road r2)
{
return r1.w < r2.w;
}
void show_way(int x)
{
if(T[x].f == -1) {
cout << T[x].u;
return;
}
show_way(T[x].f);
cout << "-" << T[x].u;
}
int start[11]; //每个人的起始城市
int main()
{
//读入数据
int nc,dc,nr;
cin >> nc >> dc >> nr;
for(int i=1;i<=nr;i++)
cin >> R[i].u >> R[i].v >> R[i].w;
int nj;
cin >> nj;
for(int i=1;i<=nj;i++)
cin >> start[i];
int most = pow(2,nc),Min_cost=0x3f3f3f3f,Min_pass=nc+1,FINNAL; //最终方案
bool flag;
for(int c=1;c<=most;c++) { //枚举每个city选或不选
flag = false;
for(int p=1;p<=nj;p++) { //判断每个people的起始点是否包括在内
if(!(c & (1 << (start[p]-1)))) {
flag = true;
break;
}
}
if(flag)
continue;
if(!(c & (1 << (dc-1)))) //看目标城市是否包括在内
continue;
int thr = 0;
for(int i=1;i<=nc;i++) {
if(1 & (1 << (i-1))) //看一共经过了多少城市
thr++;
}
// Kruskal建立最小生成树并计算花费
sort(R+1,R+1+nr,cmp);
for(int i=1;i<=nc;i++)
f[i] = i;
int tot=0,pass=0,cost=0;
bool con[21][21];
bool book[401];
memset(book,false,sizeof book);
memset(con,0,sizeof con);
for(int i=1;i<=nr;i++) {
if(!(c & (1 << (R[i].u-1))) || !(c & (1 << (R[i].v-1)))) //如果这条路连接的两个城市至少有一个没包括
continue;
if(Merge(R[i].u,R[i].v)) {
con[R[i].u][R[i].v] = true;
con[R[i].v][R[i].u] = true; //在最小生成树中两边是直接连通的
cost += R[i].w;
//book[i] = true; //第i条边被选用
++pass;
}
if(pass == thr-1)
break;
}
// 判断每个人是否能到达目标城市
int i;
for(i=1;i<=nj;i++) {
bool book[401];
memset(book,false,sizeof book);
flag = false;
if(start[i] == dc)
continue;
int k=0;
queue<Through > Thr;
T[k].u = start[i];
T[k].f = -1;
T[k].ord = k;
book[start[i]] = true;
Thr.push(T[k]);
k++;
while(!Thr.empty()) {
int u = Thr.front().u;
for(int x=1;x<=nc;x++) {
if(con[u][x] && !book[x]) {
if(x == dc) {
flag = true;
break;
}
book[x] = true;
T[k].u = x;
T[k].ord = k;
T[k].f = Thr.front().ord;
Thr.push(T[k]);
k++;
}
}
if(flag)
break;
Thr.pop();
}
if(!flag)
break;
}
if(i == nj+1) {
if(Min_cost > cost) {
Min_cost = cost;
Min_pass = pass;
FINNAL = c;
}
else if(Min_cost == cost) {
if(Min_pass > pass) {
Min_pass = pass;
FINNAL = c;
}
}
}
}
cout << "distance = " << Min_cost << endl;
for(int i=1;i<=nc;i++)
f[i] = i;
bool con[21][21];
memset(con,0,sizeof con);
int pass = 0;
for(int i=1;i<=nr;i++) {
if(!(FINNAL & (1 << (R[i].u-1))) || !(FINNAL & (1 << (R[i].v-1)))) //如果这条路连接的两个城市至少有一个没包括
continue;
if(Merge(R[i].u,R[i].v)) {
con[R[i].u][R[i].v] = true;
con[R[i].v][R[i].u] = true; //在最小生成树中两边是直接连通的
pass++;
}
if(pass == Min_pass)
break;
}
// bfs找路
for(int t=1;t<=nj;t++) { // 每个人
if(start[t] == dc) {
cout << dc << endl;
continue;
}
bool book[401];
memset(book,false,sizeof book);
queue<Through> Thr;
int u = start[t];
book[u] = true;
int k=0;
T[k].u = u;
T[k].ord = k;
T[k].f = -1;
Thr.push(T[k]);
k++;
bool flag = false;
while(!Thr.empty()) {
u = Thr.front().u;
for(int i=1;i<=nc;i++) {
if( con[u][i] && !book[i]) {
book[i] = true;
T[k].u = i;
T[k].ord = k;
T[k].f = Thr.front().ord;
Thr.push(T[k]);
k++;
if(i == dc) {
flag = true;
break;
}
}
if(flag)
break;
}
if( flag)
break;
Thr.pop();
}
show_way(--k);
cout << endl;
}
}