这道千分题,其实是挺有意思的一题:
提供了点(这里是junction)和点之间的距离(或代价);
要求以最短的距离(或最小代价)遍历所有的点,同时每个点可以多次访问;
初看之下,给人的感觉是图论相关的问题,比如旅行者问题、欧拉环游之类。
在思考这个问题的时候,忽然间联想到了图论中的最小生成树,虽然并不是真正要去得出最小生成树,
但是按照最小生成树所提供的思路--这点很重要--那就是图和树之间有着相当密切的关系:即使最小生
成树并不能直接解决这个问题,但是它们之间存在的这层关系的确提供了解决问题的一个有益的尝试方
向;
于是,思考进了一步,问题从“图”简化成了“树”--如何把当前这个问题采用树的结构和方法表达出
来:树的根节点,很自然地想到了由问题中旅行的起始节点来表达;然后,随着节点的不断加入,树就
自然地生成,此处的关键在于如何生成,或者说节点加入的规则,以及每个节点为了适应这个规则,所
必须持有的相关属性信息:最直接的,父子节点之间的关系需要维护,从父节点到子节点的距离(或代
价)必须要保留,其次,考虑到如果每个节点都维护相关的距离(代价)信息,那么从当前节点到根节
点的代价也就可以由此递推得出,进一步,我们所要求出的最短路径(或最小代价)不就可以从上述这
些节点中维护的距离信息中得出吗?这是非常关键的一步,它把当前我们构建的数据结构和问题的要求
之间建立起了相当直接的联系。这说明我们目前思考的方向是有价值的而且极有可能顺着这个方向前行
,可以得出相当不错的结果。
显然,既然要求最短路径(最小代价),那么我们目前构建出的这颗Junction树(因为其上的节点在题
中的物理含义是代表Junction,那这里我们就姑且称其为Junction Tree),树上的每个节点也应当保留
在其之下的子树的最短路径(最小代价),这就相当于把每个节点都作为根节点,然后求出各条子路径
的代价,并比较得出最短路径(最小代价),以及在这条最短路径上的直接子节点;
每加入一个子节点,就要对上述已构建出的这些数据结构中的信息进行维护,以调整每个节点当前的最
短路径代价和相应这条路径上的直接子节点;当所有原“图”中的“边”信息,也就是
(fromJunction,toJuction,ductLength)所代表的(起始点,终止点,长度代价),都按照上述方案加入
Juction Tree之后,我们可以知道从最初的起始节点(也就是Junction Tree的根节点)到最终节点的(
Junction Tree上的某条路径上的叶子节点)的最短(最小代价)路径了。
对于Juction Tree这个ADT抽象数据结构的具体实现,考虑到优先队列中二叉堆的经典实现往往使用数组
,同时也为了符合TC SRM一贯的简捷明快的程序设计风格,我们这里同时使用几个数组来维护前述构建
出的数据结构。
//////////////////////////////////////////////////////////////////////////////////////////
#include<cstdlib>
#include<vector>
#include<set>
using namespace std;
const int NIL = -1;
const int MAX = 50;
int Cost[MAX];
int ParentNode[MAX];
int MaxSubNode[MAX];
int MaxSubCost[MAX];
class PowerOutage
{
public:
int estimateTimeOut(vector<int> fromJunction, vector<int> toJunction, vector<int>
ductLength)
{
if (!CheckParameter(fromJunction, toJunction, ductLength)) return NIL;
Ini();
int count = fromJunction.size();
for (int i = 0; i < count; i++)
{
AddNode(fromJunction[i], toJunction[i], ductLength[i]);
}
return CalculateMinCost(fromJunction, toJunction, ductLength);
}
private:
void Ini()
{
memset(Cost, NIL, sizeof(int) * MAX);
memset(ParentNode, NIL, sizeof(int) * MAX);
memset(MaxSubNode, NIL, sizeof(int) * MAX);
memset(MaxSubCost, 0, sizeof(int) * MAX);
}
bool CheckParameter(const vector<int>& fromJunction, const vector<int>& toJunction,
const vector<int>& ductLength)
{
if (fromJunction.size() != toJunction.size() || toJunction.size() !=
ductLength.size())
return false;
return true;
}
void AddNode(int parent, int child, int cost)
{
if (parent < 0 || child < 0 || cost < 0) return;
Cost[child] = cost;
ParentNode[child] = parent;
int curParent = parent, curChild = child;
bool adjustParentCost = true;
while (adjustParentCost && curParent != NIL)
{
int candidateParentMaxSubCost = Cost[curChild] + MaxSubCost
[curChild];
if (MaxSubCost[curParent] < candidateParentMaxSubCost)
{
MaxSubCost[curParent] = candidateParentMaxSubCost;
MaxSubNode[curParent] = curChild;
curChild = curParent;
curParent = ParentNode[curParent];
}
else
{
adjustParentCost = false;
}
}
}
int CalculateMinCost(const vector<int>& fromJunction, const vector<int>&
toJunction, const vector<int>& ductLength)
{
int len = fromJunction.size();
int minCost = 0;
set<int> minCostPath;
minCostPath.insert(0);
int curNode = MaxSubNode[0];
while(curNode != NIL)
{
printf("%d;",curNode); // print the min cost path
minCostPath.insert(curNode);
curNode = MaxSubNode[curNode];
}
for (int i = 0; i < len; i++)
{
if (minCostPath.find(toJunction[i]) == minCostPath.end())
minCost += 2 * ductLength[i];
else
minCost += ductLength[i];
}
return minCost;
}
};