ALGO 178 The Traveling Judges Problem 满分 答案

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。

心得与体会

  1. 思路:用的是最小生成树+枚举的方法。由于部分节点可能不会用到,枚举不会用到的节点,并把这些节点屏蔽掉,用prim算法得出屏蔽掉这些节点的时候的最小生成树,取最优。
  2. 但如果完全按上面的方法做,有一个测试点会超时。所以这里有一个小改进:先不屏蔽任何节点,生成一个最小生成树,称之为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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值