FOJ 1207 半数集问题

原题:

http://acm.fzu.edu.cn/problem.php?pid=1207

解题分析:

要注意的有三点:

1)  “从 N 开始依次产生半数集 ……”. 要注意 ” 依次 ” 的表述 , 说明半数集中元素的产生是有先后顺序的 . 这对后面的排除重复元素很重要 .

2)  “在 N 的左边加上一个自然数 , 但该自然数不能超过最近添加数的一半 .” 这是构造解集的重要条件 , 引导我们使用递归方法解决此题 ..

3)  “半数集不是多重集 . 集合中已经有的元素不再添加到集合中 “. 配合 1), 就可以了解产生重复的原因 .

重复分析:  假设 N=50. 那么会出现如下情况 :

          50 -> 12 50

          50 -> 2 50 -> 1 2 50  (与之前产生的 1250 重复 , 重复一次 )

其实, 重复造成的问题不止如此 , 考虑下面的情况 :

50 -> 48 50

50 -> 8 50 -> 4 8 50 

4 8 50与已经产生的  48 50 重复 . 这不仅造成自身不能添加到半数集中 , 而且使得由 4 8 50 扩展形成的  2 4 8 50 和 1 2 4 8 50 都不会出现在半数集中 .

 
所以, 扣除重复元素的操作要注意两个问题 :

1)  产生重复的条件.
2)  应该扣除多少个元素.

产生重复的条件:  根据题意 , 0<N<201,  所以 , 0<N/2<=100. 第二次添加的数肯定是个两位数 . 也就是说 , 重复是由于一个两位数和两个一位数的冲突造成的 . 由添加数的规则可得 , 当添加的数为 i 时 , 若  i > 9 && ( (i / 10) <= (1 % 10) / 2).

应该扣除的个数:  由构造的顺序性可知 , 与一个两位数构造成的半数集元素重复的元素不能添加到半数集中 .  要扣除的个数就是由最左边的一位数能够产生的半数集数量 . 例如 ,

    50 -> 48 50

    50 -> 8 50 -> 4 8 50

要扣除的数量就是4 能够产生的半数集元素个数。

源码:(VC++)

#include <stdio.h> int total; //数组中的值 , 表示与下标等值的数所能产生的半数集的元素个数 int map[] = {1, 1, 2, 2, 4, 4, 6, 6, 10, 10}; void nextSet(int n); int main() { //freopen(“input.txt”, “r”, stdin); int n; while(scanf(“%d”, &n) != EOF) { total = 1; nextSet(n); printf(“%d/n”, total); } return 0; } void nextSet(int n) { if(n > 1) { int t = n / 2; for(int i = 1; i <= t; i++) { nextSet(i); total++; if(i > 9 && ( (i / 10) <= (i % 10) / 2)) { total -= map[(i / 10)]; //扣除重复 } } } }

    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/lostaway/article/details/5604835
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞