uva 1040(最小生成树+搜索)(World Final C题) Apare_xzc
uva 1040 解题报告
xzc 2019/3/27
vjudge链接:The Traveling Judges Problem
题意:
CSDN上找的中文题面
问题描述
一组人要担任在一个特定城市举办的比赛的评委,他们需要找到最便宜的租车方式使得每个人都到达目标城市。他们观察发现,如果几个人在旅途的某一段坐同一辆租的车,就可以减少总费用。你的任务就是找出这些人应该采取的路线使得租车的总费用最小。
我们假定:
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
---------------------
作者:CSDN知名群众
来源:CSDN
原文:https://blog.csdn.net/weixin_42765557/article/details/87967603
版权声明:本文为博主原创文章,转载请附上博文链接!
思路:
- 思路大致是求最小生成树。
- 但是答案有限制,就是在总距离最短的情况下,要求途径子节点的集合最小,如果集合最小(结点个数),那么要求集合排序后字典序最小
- 我一开始直跑了一遍prim,但是Wa了,可能是字典序的问题。
- 我后来dfs出子图的结点集合,然后以所有裁判那个共同的目的地为根结点,prim跑出这个子图的MST,然后判断是否可达每个裁判起点,若可达,更新答案。
我的代码:
/* #18948904 | XuZhichao's solution for [UVA-1040] [Problem B] Accepted 3175 0ms C++11 5.3.0 2019-03-27 16:52:53 Shared */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <vector>
#define Mst(a,b) memset(a,(b),sizeof(a))
#define For(i,a,b) for(register int i=(a);i<=(b);++i)
#define LL long long
using namespace std;
const int maxn = 25;
const LL INF = 0x3f3f3f3f3f3f3f3f;
LL resDis,ans;
int resNode;
LL dis[50][50]; ///邻接矩阵存图
LL D[maxn]; ///到生成树的距离
int pre[maxn],ed;//ed为目的地
int ansPre[maxn];
bool vis[maxn];
bool isSt[maxn]; ///isSt[i]==true 表示第i个地方有人出发
void getPath(int *,int);
int s[25],totPerson;
bool inSubgraph[maxn];
bool prim(int ed,int n,int tot,int numOfNode); //数的根节点,节点总个数,起点的总数
void solve(int n)
{
//For(i,1,n) cout<<s[i];cout<<endl;
int numOfNode = 0; //子图的节点数
Mst(inSubgraph,0);
For(i,1,n) if(s[i]) ++numOfNode,inSubgraph[i] = true;
if(prim(ed,n,totPerson,numOfNode))
{
For(i,1,n) ansPre[i] = pre[i];
resDis = ans;
}
}
int numOf1;
void dfs(int x,int n)
{
if(x==n+1)
{
solve(n);
return;
}
s[x] = 1;
dfs(x+1,n);
if(!isSt[x]&&x!=ed) s[x] = 0,dfs(x+1,n);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,nedge,nperson,u,v,ca=0; LL d;
while(scanf("%d",&n)!=EOF&&n!=-1)
{
//if(ca) printf("\n");
printf("Case %d: distance = ",++ca);
Mst(isSt,false);
Mst(dis,0x3f);
resDis = INF;
scanf("%d%d",&ed,&nedge);
For(i,1,nedge)
{
scanf("%d%d%lld",&u,&v,&d);
dis[u][v] = d;
dis[v][u] = d;
}
scanf("%d",&nperson);
vector<int>person;
For(i,1,nperson)
scanf("%d",&u),person.push_back(u),isSt[u] = true;
totPerson = 0;
For(i,1,n) totPerson += isSt[i];
dfs(1,n);
printf("%lld\n",resDis);
for(auto &x:person)
{
getPath(ansPre,x);
}
printf("\n");
}
return 0;
}
void getPath(int * pre,int x)
{
printf(" %d",x);
while(x!=ed)
{
x = pre[x];
printf("-%d",x);
}
printf("\n");
}
bool prim(int ed,int n,int tot,int numOfNode) //起点,n,必须到达的节点的总数,图中节点的总数,
{
ans = 0;
Mst(vis,0);
For(i,1,n)
D[i] = dis[ed][i], pre[i] = ed; //初始化
//D[ed] = 0;
vis[ed] = true;
int MinNode = -1;
int numOfNodeInMST = 0;
while(true)
{
LL nowDis = INF;
For(i,1,n)
{
if(vis[i]||!inSubgraph[i]) continue;//已经被访问或者不在这个子图中
if(D[i]<nowDis)
{
MinNode = i;
nowDis = D[i];
}
}
if(MinNode==-1) break;
ans += nowDis;
vis[MinNode] = true;
++numOfNodeInMST;
For(i,1,n)
{
if(vis[i]) continue;
if(dis[MinNode][i]<D[i]||dis[MinNode][i]==D[i]&&MinNode<pre[i])
{
D[i] = dis[MinNode][i];
pre[i] = MinNode;
}
}
if(isSt[MinNode])
{
--tot;
if(tot==0)
{
break;
}
}
}
if(tot) return false; //有的起点不可达
if(ans<resDis||ans==resDis&&numOfNodeInMST<resNode) return true;
return false;
}