ALGO 178 The Traveling Judges Problem
url:http://lx.lanqiao.cn/problem.page?gpid=T485
问题描述
一组人要担任在一个特定城市举办的比赛的评委,他们需要找到最便宜的租车方式使得每个人都到达目标城市。他们观察发现,如果几个人在旅途的某一段坐同一辆租的车,就可以减少总费用。你的任务就是找出这些人应该采取的路线使得租车的总费用最小。
我们假定:
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。
心得与体会
- 思路:用的是最小生成树+枚举的方法。由于部分节点可能不会用到,枚举不会用到的节点,并把这些节点屏蔽掉,用prim算法得出屏蔽掉这些节点的时候的最小生成树,取最优。
- 但如果完全按上面的方法做,有一个测试点会超时。所以这里有一个小改进:先不屏蔽任何节点,生成一个最小生成树,称之为T0。然后屏蔽掉某个点u的时候,将T0中u及其子孙节点全部删去,并将此记为Tu。之后对于Tu,用prim算法,但其外层循环只迭代k次,k为(点的数量-屏蔽掉点的数量-Tu中节点的数量)。这样就可以用利用到之前的信息,用尽量少的步骤得到屏蔽到某个点的最小生成树。(参考了刘汝佳大神kruskal的用已有信息的想法)
/*
思路:先得到一颗整个图的最小生成树
然后用mask,枚举需要屏蔽(即不经过)的点
对于每个情况,将被屏蔽的节点及其对应的部分生成树子树删掉
然后在删去部分的基础上,不用被屏蔽掉的点获得最小生成树
然后记录路径
由于需要对被屏蔽的点进行枚举,故采用prim算法
*/
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
#include<ctime>
using std::scanf;
using std::printf;
using std::memset;
using std::vector;
using std::memcpy;
using std::pow;
using std::sort;
using std::clock;
#define NC 20
#define NJ 10
int nc;
int nj;
int m;//边数
int dist[NC][NC];
int loc[NJ];//开始位置
int desc;
bool have_people[NC];
char mask[NC];
int status_index;//枚举的状态数为2的status_index次方。该值为没有人的城市的值。
class Method{
public:
//存储一个方案
bool visited[NC];
bool in_path[NC][NC];
int pre[NC];
int len;//树长度
int use_node_num;
Method(){
memset(visited,0,sizeof(visited));
memset(in_path,0,sizeof(in_path));
memset(pre,0x3f,sizeof(pre));
len=0;
use_node_num=0;
}
Method(const Method &other){
memcpy(visited,other.visited,sizeof(visited));
memcpy(in_path,other.in_path,sizeof(in_path));
memcpy(pre,other.pre,sizeof(pre));
len=other.len;
use_node_num=other.use_node_num;
}
bool operator<(Method& other){
if(len<other.len) return true;
else if(len==other.len&&use_node_num<other.use_node_num) return true;
return false;
}
};
vector<Method> result;
Method preprocess(Method& src){
int i,j;
Method result;
vector<int> should_be_delete;
int size;
int x;
should_be_delete.reserve(nc);
result=src;
//printf("src.pre[1]=%d result.pre[1]=%d\n",src.pre[1],result.pre[1]);
for(i=0;i<nc;i++){
for(j=i;j!=0x3f3f3f3f;j=result.pre[j]){
//printf("j=%d pre[j]=%d\n",j,result.pre[j]);
if(mask[j]){
//printf("enter the if\n");
//将该点删除
should_be_delete.push_back(i);
break;
}
}
}
size=should_be_delete.size();
/*printf("shoud be deleted:");
for(i=0;i<size;i++){
printf("%d ",should_be_delete[i]);
}
printf("\n");*/
for(i=0;i<size;i++){
x=should_be_delete[i];
result.visited[x]=false;
result.use_node_num--;
result.len-=dist[x][result.pre[x]];
result.in_path[x][result.pre[x]]=false;
result.in_path[result.pre[x]][x]=false;
}
for(i=0;i<size;i++){
x=should_be_delete[i];
result.pre[x]=0x3f3f3f3f;
}
return result;
}
Method prim(Method& status){
int min;
int mini,minj;
int i,j,k;
int n=nc-status.use_node_num;
//printf("enter prim\n");
for(k=0;k<n;k++){
//printf("k=%d\n",k);
min=0x3f3f3f3f;
mini=-1;
minj=-1;
for(i=0;i<nc;i++){
if(status.visited[i]){
for(j=0;j<nc;j++){
if(!status.visited[j]&&!mask[j]&&dist[i][j]<min){
mini=i;
minj=j;
min=dist[i][j];
}
}
}
}
if(mini!=-1){
//printf("mini=%d minj=%d\n",mini,minj);
status.visited[minj]=true;
status.in_path[mini][minj]=true;
status.in_path[minj][mini]=true;
status.pre[minj]=mini;
status.len+=min;
status.use_node_num++;
}else{
break;
}
}
return status;
}
bool plus_mask(){
//mask++
int i;
bool flag;
int rest;
flag=true;//是否可以++
for(i=0;i<nc;i++){
if(!have_people[i]&&desc!=i&&mask[i]==0){
flag=false;
break;
}
}
if(flag) return false;
mask[0]++;
rest=0;
i=0;
do{
//模拟进位
if(have_people[i]||i==desc){
rest+=mask[i];
mask[i]=0;
}else{
mask[i]+=rest;
rest=mask[i]/2;
mask[i]%=2;
}
i++;
}while(i<nc&&rest!=0);
return true;
}
void show_mask(){
int i;
printf("mask:");
for(i=0;i<nc;i++) printf(" %d",mask[i]);
printf("\n");
}
int get_node_num(){
int i;
int count;
count=0;
for(i=0;i<nc;i++) if(!mask[i]) count++;
return count;
}
bool is_ok(Method status){
int i;
if(status.len==-1) return false;
for(i=0;i<nj;i++){
if(!status.visited[loc[i]]) return false;
}
return true;
}
void enum_span_tree(){
int i;
char mask[NC];
Method temp;
Method src;
memset(mask,0,sizeof(mask));
src.visited[desc]=true;
src.use_node_num=1;
src=prim(src);
result.push_back(src);
while(plus_mask()){
//show_mask();
temp=preprocess(src);.
temp=prim(temp);
if(is_ok(temp)){
//printf("ok\n");
result.push_back(temp);
}
}
}
int main(){
int i,j;
int u,v,d;
int temp;
int len;
clock_t start;
Method min;
int size;
start=clock();
scanf("%d%d%d",&nc,&desc,&m);
desc--;
memset(dist,0x3f,sizeof(dist));
memset(have_people,0,sizeof(have_people));
for(i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&d);
dist[u-1][v-1]=d;
dist[v-1][u-1]=d;
}
scanf("%d",&nj);
status_index=nc;
for(i=0;i<nj;i++){
scanf("%d",&temp);
loc[i]=temp-1;
if(!have_people[temp-1]){
have_people[temp-1]=true;
status_index--;
}
}
enum_span_tree();
min=result[0];
size=result.size();
for(i=1;i<size;i++){
if(result[i]<min) min=result[i];
}
result[0]=min;
printf("distance = %d\n",result[0].len);
for(i=0;i<nj;i++){
j=loc[i];
printf("%d",j+1);
while(j!=desc){
j=result[0].pre[j];
printf("-%d",j+1);
}
printf("\n");
}
//printf("use time:%.2f\n",(clock()-start)/(double)CLOCKS_PER_SEC);
return 0;
}
github:https://github.com/yexinhua1998/algo/tree/master/lanqiao-contest/ALGO/ALGO178