多点两两相连问题
程序员文章站
2022-07-13 15:42:53
...
问题描述
- 如下图,给定偶数个点,两两之间连线,规则为:
- 每个点仅能与另外一个点连接,如图:不准有1与3类似的连接
- 不能有剩余的点未与其他点连接。要求输入偶数个点,输出有多少中连接方式?
问题分解
- 当有两个点时:只有1-2连接的情况
- 当有四个点时:会有1-2、3-4与1-4、2-3连接的情况,不能出现1-3这种交叉连接的情况
- 当有六个点时:
- 首先固定点1,按照规则,能与1连接的点分别为:2、4、6,从以上三步总结下来,能与1点连接的点都是编号为偶数的节点
- 假设1、2连接,那剩下的就是3、4、5、6节点,即固定1和2两个点,把6个点分隔成两个点(1, 2)和另外四个点(3, 4, 5, 6)的子问题
- 假设1、4连接,那剩下的就是(2, 3)、(5, 6)节点连接的问题
- 问题分解到这里,我们便能想到:这是一个把复杂问题分解成子问题的需求,应用动态规划即可,动态规划的关键点在于找到子问题的解如何组合才能得到最终的结果
- 有了上述的思路,那接下来的分析便是要找到分解复杂问题的公式,分解到6个点的时候,可以得出一个公式:f(6) = 2*f(4)*f(2) + f(2)*f(2) ,即:步骤3的分析
- 从F部推到出的公式显然还不能得到解决,因为还不能用其解决8个、10个点的问题,那继续分析8个点的场景:
- 固定点1,1能和2、4、6、8连接:1和2连接将问题分解为(1, 2)、(3, 4, 5, 6, 7, 8)连接的问题
- 1和4连接,将问题分解为(1, 4)、(2, 3)、(5, 6, 7, 8)连接的问题
- 1和6连接,将问题分解为(1, 6)、(2, 3, 4, 5)、(7, 8)的连接
- 1和8连接,将问题分解为:(1, 8)、(2, 3, 4, 5, 6, 7)的连接
- 那总结公式为:f(8) = 2*f(6)*f(2) + f(2)*f(4) + f(4)*f(2)
- 至此,依据以上的判断,可以总结出的规律为:
- 给定N个点,当固定两个点时,剩余的N-2个点可以被分解成:固定部分2 * f(N-2)*f(2)以及子问题X个点与Y个点的连接,其中X+Y=N-2,X,Y < N-2
- 例如若N为10,则X、Y可取为:2&6、4&4、6&2,
- 推导出最终公式为:f(n)=f(2m)=2*f(2m-2)*f(2) + f(2i)*f(2m-2i-2) + … f(2i)*f(2), i的取值范围是[1,m-2]
程序实现
#include <iostream>
uint32_t ChoicesofSpotConnection(int spot_num) {
int* choices = new int[spot_num + 1];
choices[0] = 0;
choices[2] = 1;
choices[4] = 2;
choices[6] = 2 * choices[4] + choices[2] * choices[2];
uint32_t count_time = spot_num / 2;
for (uint32_t m = 4; m <= count_time; ++m) {
choices[2 * m] = 2 * choices[2 * m - 2];
for (uint32_t i = 1; i <= (m - 2); ++i) {
choices[2 * m] += choices[2 * i] * choices[2 * m - 2 * i - 2];
}
std::cout << "2*m: " << 2 * m << ", choices: " << choices[2 * m] << std::endl;
}
uint32_t rtn = choices[spot_num];
delete[] choices;
return rtn;
}
int main(int argc, char* argv[]) {
int rtn = ChoicesofSpotConnection(12);
std::cout << "choices:" << rtn << std::endl;
return 0;
}