一、题目大意
有N条双向边将城市里的小镇连通起来,寻找出到达其他所有小镇距离之和最短的小镇,并输出该距离之和。
二、解题思路
用Set记录有哪些小镇,用Vector记录每个小镇与其他小镇之间相连的边,遍历Set,用Dijkstra算法求解每个小镇到其他小镇的距离之和。然后取最小的就可以。
三、代码
#include <iostream>
#include <queue>
#include <vector>
#include <set>
using namespace std;
//用来求出最短路径的结构体
struct Node {
//节点编号
int nodeId;
//起点到达该节点的最短距离
int theShortestDistanceToTheNode;
//重载 < 运算符
bool operator<(const Node& node) const {
return this->theShortestDistanceToTheNode<node.theShortestDistanceToTheNode;
}
//构造方法
Node(int _nodeId,int _theShortestDistanceToTheNode):nodeId(_nodeId),theShortestDistanceToTheNode(_theShortestDistanceToTheNode) {}
};
//定义比较器
struct NodeComparator {
//比价器中为左边大于右边,则优先队列会将数值小的放在对首
bool operator()(const Node& nodeLeft,const Node& nodeRight) {
return nodeLeft.theShortestDistanceToTheNode>nodeRight.theShortestDistanceToTheNode;
}
};
//顶点的一条边
struct edge {
//边的端点的节点编号
int endPointNodeId;
//变的长度
int length;
edge(int _endPointNodeId,int _length):endPointNodeId(_endPointNodeId),length(_length) {}
};
//最多的节点个数
const int MAX_NODE_COUNT=16;
//节点个数
int nodeCount;
//边的个数
int edgeCount;
//每一个点都有一个vector,存放它所能够到达的边
vector<edge> edgeOfNodeId[MAX_NODE_COUNT];
//代表存放图中节点的队列
set<int> nodeIdSet;
//到达某个节点的最小距离
int distanceToNodeId[MAX_NODE_COUNT];
//无穷大
const int inf=0x3f3f3f3f;
//迪杰斯特拉算法
void dijkstra(int startNodeId) {
priority_queue<Node,vector<Node>,NodeComparator > priorityQueue;
//初始化距离都为无穷大
for(int i=0; i<MAX_NODE_COUNT; i++) {
distanceToNodeId[i]=inf;
}
//起点到本身的距离为0
distanceToNodeId[startNodeId]=0;
//将起点放入优先队列
priorityQueue.push(Node(startNodeId,0));
while(!priorityQueue.empty()) {
//当前最短距离已经确定的点
Node currentNode=priorityQueue.top();
//出队
priorityQueue.pop();
//如果起点到达该点的距离已经被再次更新过,则执行下一次循环
if(distanceToNodeId[currentNode.nodeId]<currentNode.theShortestDistanceToTheNode) {
continue;
}
//遍历当前节点的所有边
for(int edgeId=0; edgeId<edgeOfNodeId[currentNode.nodeId].size(); edgeId++) {
//取出一条边
edge currentEdge=edgeOfNodeId[currentNode.nodeId][edgeId];
//如果, 距离(起点->当前边终点) > 距离(起点->跳板节点->当前边终点) 那么进入if
if(distanceToNodeId[currentEdge.endPointNodeId]>distanceToNodeId[currentNode.nodeId]+currentEdge.length) {
//更新起点到当前边终点的距离
distanceToNodeId[currentEdge.endPointNodeId]=distanceToNodeId[currentNode.nodeId]+currentEdge.length;
//该点的最短路径已经确定,放入队列中。再去作为跳板节点更新其他的距离
priorityQueue.push(Node(currentEdge.endPointNodeId,distanceToNodeId[currentEdge.endPointNodeId]));
}
}
}
}
void getEdgeCountAndNodeCount() {
scanf("%d",&edgeCount);
nodeCount=edgeCount;
}
void getInputGraphData() {
//起始节点
int startNodeId;
//终止节点
int endNodeId;
//边的长度
int lengthOfEdge;
for(int i=0; i<edgeCount; i++) {
//依次输入两个端点和边的长度
scanf("%d%d%d",&startNodeId,&endNodeId,&lengthOfEdge);
//左端点添加这条边
edgeOfNodeId[startNodeId].push_back(edge(endNodeId,lengthOfEdge));
//右端点添加这条边
edgeOfNodeId[endNodeId].push_back(edge(startNodeId,lengthOfEdge));
//将节点编号放入集合,方便以后去重遍历
nodeIdSet.insert(startNodeId);
nodeIdSet.insert(endNodeId);
}
}
//初始化数据结构
void initEdgeArray() {
//将所有节点的边的清掉
for(int i=0; i<MAX_NODE_COUNT; i++) {
edgeOfNodeId[i].clear();
}
//清空节点集合
nodeIdSet.clear();
}
//计算节点到其他节点的距离之和
int calculateSumOfDistance() {
int sumOfDistance=0;
set<int>::iterator ite;
for(ite=nodeIdSet.begin(); ite!=nodeIdSet.end(); ite++) {
int nodeId=*ite;
sumOfDistance+=distanceToNodeId[nodeId];
}
return sumOfDistance;
}
int main() {
while(true) {
//输入节点数量
getEdgeCountAndNodeCount();
//如果输入为0则程序结束
if(edgeCount==0&&nodeCount==0) {
break;
}
//初始化数据结构
initEdgeArray();
//输入图的信息
getInputGraphData();
//定义初始化最佳节点
int currentMinDistanceNodeId=0;
//最佳节点到其他节点的距离和
int currentMinDistance=inf;
//遍历Set
set<int>::iterator ite;
for(ite=nodeIdSet.begin(); ite!=nodeIdSet.end(); ite++) {
//取到一个节点ID
int nodeId=*ite;
//调用迪杰斯特拉算法
dijkstra(nodeId);
//求出距离之和
int sumOfThisNode=calculateSumOfDistance();
//如果距离之和小于当前的最小距离,记录节点信息和最小距离
if(sumOfThisNode<currentMinDistance) {
//最小距离
currentMinDistance=sumOfThisNode;
//节点ID
currentMinDistanceNodeId=nodeId;
}
}
//输入最佳节点Id和最小的距离
printf("%d %d\n",currentMinDistanceNodeId,currentMinDistance);
}
return 0;
}
四、总结
掌握更多的数据结构,能够更好的解题,也有助于平时的开发工作。