您的位置:首页 > 其它

【解题报告】pku数算mooc 第8周 图 兔子与星空(poj 5442)

2017-11-22 19:13 375 查看
描述

很久很久以前,森林里住着一群兔子。兔子们无聊的时候就喜欢研究星座。如图所示,天空中已经有了n颗星星,其中有些星星有边相连。兔子们希望删除掉一些边,然后使得保留下的边仍能是n颗星星连通。他们希望计算,保留的边的权值之和最小是多少?

兔子与星空示意图



输入

第一行只包含一个表示星星个数的数n,n不大于26,并且这n个星星是由大写字母表里的前n个字母表示。接下来的n-1行是由字母表的前n-1个字母开头。最后一个星星表示的字母不用输入。对于每一行,以每个星星表示的字母开头,然后后面跟着一个数字,表示有多少条边可以从这个星星到后面字母表中的星星。如果k是大于0,表示该行后面会表示k条边的k个数据。每条边的数据是由表示连接到另一端星星的字母和该边的权值组成。权值是正整数的并且小于100。该行的所有数据字段分隔单一空白。该星星网络将始终连接所有的星星。该星星网络将永远不会超过75条边。没有任何一个星星会有超过15条的边连接到其他星星(之前或之后的字母)。在下面的示例输入,数据是与上面的图相一致的。

输出

输出是一个整数,表示最小的权值和

样例输入

9

A 2 B 12 I 25

B 3 C 10 H 40 I 8

C 2 D 18 G 55

D 1 E 44

E 2 F 60 G 38

F 0

G 1 H 35

H 1 I 35

样例输出

216

原题链接:http://dsalgo.openjudge.cn/huawen08/2/

解题思路:

这是一个最小生成树问题,即用最小的总权值使得图中所有的节点连通。

按题目给出的条件来说的话,这个星空图是属于稀疏图,因此用Kruskal算法是比较好的,给出的代码也是使用Kruskal算法。

保存图中各边的信息的话,由于题目最后只求最小总权值,我自我开发了一种
multimap <int,pair<char,char>>
的表示方法,因为其实每条边用完就不需要再用了。算是开发一种思路吧,注意要使用multimap因为边的权值可能相等。

当然啦其实就用邻接矩阵就挺好的,也完全ok。由于要做等价类归并,所以还要写并查算法的ParentTreeNode,ParentTree什么的一堆东西。

(所以我发现用prim算法写的东西要少很多···)

以下代码:

#include<iostream>
#include<map>
#include<string>
using namespace std;
//通过map保存各条边的数据...
multimap<int, pair<char, char> > itoc;
int leftn;//记录是否归并成了一个等价类
//本题星空图属于稀疏图,采用kruskal算法,
//需要进行等价类的归并,采用父指针表示的并查算法
class partreenode
{
public:
int v;
partreenode * parent=NULL;
};
class partree
{
public:
partreenode * array;
int size;
partree(const int Size) //构造
{
size = Size;
array = new partreenode[size];
for (int i = 0; i < size; ++i)
array[i].v = i; //0对应A,1对应B这样的形式保持同构
}
partreenode * Find(partreenode *node)
{
partreenode* pointer = node;
while (pointer->parent != NULL)
pointer = pointer->parent;
return pointer;
}
void Union(int i, int j)
{
partreenode *pi = Find(&array[i]);
partreenode *pj = Find(&array[j]);
if (pi != pj)
{
pj->parent = pi;
}
}//本题中都采用的简陋的函数实现,用不到的就没写- -
bool Different(int i, int j)
{
partreenode *pi = Find(&array[i]);
partreenode *pj = Find(&array[j]);
return pi != pj;
}
};
int main()
{
int n;
cin >> n;
leftn = n;
partree tree(n);//归并用的等价类
for (int i = 0; i < n-1; i++)
{
char t; cin >> t;
int en; cin >> en;
char te; int len;
for (int j = 0; j < en; ++j)
{
cin >> te;
cin >> len; //输入各边- -map怎么用忘得差不多了,可能不用写成这么复杂的形式吧
itoc.insert(pair<int,pair<char, char>>(len,pair<char, char>(t, te)));
}
}
//下面开始真正做归并0.0
int ans = 0; //记录权值结果
while (leftn > 1) //等价类只剩一个的时候结束
{
//itoc中按权值从小到大存储了各各边
multimap<int, pair<char, char> >::iterator it = itoc.begin();
int s1, s2 = 0;
while (it != itoc.end())
{
s1 = it->second.first - 65;
s2 = it->second.second - 65;
if (tree.Different(s1, s2)) //s1 s2不在同一等价类,则做归并
{
tree.Union(s1, s2);
--leftn;
ans += it->first; //加入一条边,只问权值,所以只记录权值和
}
++it;//不满足条件或操作完成则直接进入下一条边
}
}
cout << ans;
//system("pause");
return 0;
}


转载请注明原文链接。

想要浏览更多的学习内容可以看看博主更多其他的博文哦~

http://blog.csdn.net/weixin_38062864
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: