有权重的无向图最大割
如图所示,现有无向图G=(V,E)。
设U是V的一个子集。对任意的顶点u,v: 如果(u,v)这条边属于E的一条边,此时u在U中,那么v一定不在U中,这样的边就称为顶点集合U的一个割边,所有的割边集合将集合V分成了两个顶点子集合。顶点集合U对应的所有的割边就构成图G的一个割。
最大割就是指含割边最多的割。
下图为这个无向图G的一个最大割,u集合为{1,6,8,9},v集合为{2,3,4,5,7,10},cut为8:
编程要求及思路
数据输入
第一行2个整数 n,e表示顶点个数和边个数。
第二行开始对应的数据分别为: 点1 点2 边(顶点编号从1-n)
上图G所对应的输入文件: simple.txt
1 | 10 14 |
数据输出
第一行是最大割的边数。
第二行是顶点集的向量对应1-n,0表示不在集合,1表示在集合。
1 | vertex number:10 |
解题思想 (BFS)
这道题也是一个子集选取问题,所以它的解空间树是一个子集树。
**这个集合n个元素,每个元素可以有选取或不选取2种选择,所以共用2^n个子集。**这里用分支界限法。
分支界限法:类似宽度优先搜索,它每次都取一个节点,然后把它的所有的子节点都扩展,然后加到队列中,所以说,分支界限法,这种扩展方式导致每个节点每次最多一次扩展机会,它在扩展它的分支的时候,根据一定的界限条件,或淘汰不可能产生最优解的分支。
在这里我们用优先级队列(priority queue) + 分支界限法进行编程:
- 建立一个HeapNode的结构体,重载operator < 让节点在优先队列中按照cut值从大到小排序。
- 建立头结点,初始头结点的深度depth = 0, cut = 0, edges = e;
- 初始继承头结点,将depth = 0的结点放入割集,将所有与其构成边的权重加入cut。将其压入优先级队列。
- 将满足界限条件的E也压入优先级队列。
- 从优先级队列中取出top结点设为E,从depth+1开始重复上述过程。
- 直到depth == 点数,将最优割和割集放入bestcut中。(queue中所有的node都会测试到depth == 点数为止)
代码实现(BFS)
1 |
|
解题思想 (Genetic Algorithm)
遗传算法理论:
遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最优化的搜索算法,是进化算法的一种。进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择以及杂交等等。
遗传算法模拟了达尔文的自然选择,这意味着一个人口要想生存就必须不断地自我完善。 最好的个体应该通过其健身价值来激发后代。。对于一个最优化问题,一定数量的候选解(称为个体)可抽象表示为染色体,使种群向更好的解进化。传统上,解用二进制表示(即0和1的串),但也可以用其他表示方法。进化从完全随机个体的种群开始,之后一代一代发生。在每一代中评价整个种群的适应度,从当前种群中随机地选择多个个体(基于它们的适应度),通过自然选择和突变产生新的生命种群,该种群在算法的下一次迭代中成为当前种群。
遗传算法步骤:
1)产生初始种群
2)获取第一人口的健身价值并记录最佳个体
3)通过适应性选择父母,并从选定的父母中抚养孩子,变异新一代的基因(基因的交叉和变异)
4)更新新人群的适应度并记录最佳个人
5)新一代换代(基因的交叉和变异)
遗传算法的结构:
1)基因型结构:int cut, float fitness(适应度), int gene[1000]
2)染色体设计:设置整数类型顶点数数组,0表示不属于集合S,1表示属于集合S。
3)选择:使用适应度值随机生成父索引
4)交叉:从0到顶点之间随机选择一个数字,将父母基因以此索引为基准分开,然后将父母基因重组成为子代基因。
5)突变:使用突变率改变基因,如果基因为1,则将其更改为0,如果基因为0,则将其更改为1。
代码实现(Genetic Algorithm)
1 |
|
Linux下运行C++程序
- 新建一个文件夹,添加simple.txt和maxcut.cpp
- 运行命令 g++ -o exe maxcut.cpp
- 确认文件夹中生成exe文件后,运行命令 ./exe