| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 4588 人关注过本帖
标题:关于鬼谷子问徒
只看楼主 加入收藏
泉此方
Rank: 1
等 级:新手上路
帖 子:89
专家分:0
注 册:2008-6-10
收藏
得分:0 
以上是终结版本!



" border="0" />[color=white]

#ifdef _LOLICON_
#include"Loli"  //http://
#endif
2008-06-12 16:18
asb124
Rank: 1
等 级:新手上路
帖 子:2
专家分:0
注 册:2008-6-9
收藏
得分:0 
和与积小于100有唯一解是13和4 如果和或者积大于100则多解

[[it] 本帖最后由 asb124 于 2008-6-12 22:06 编辑 [/it]]
2008-06-12 22:03
asb124
Rank: 1
等 级:新手上路
帖 子:2
专家分:0
注 册:2008-6-9
收藏
得分:0 
则多解
2008-06-12 22:04
sunkaidong
Rank: 4
来 自:南京师范大学
等 级:贵宾
威 望:12
帖 子:4496
专家分:141
注 册:2006-12-28
收藏
得分:0 
ls你看懂燕子代码了吗?

学习需要安静。。海盗要重新来过。。
2008-06-12 22:23
sunkaidong
Rank: 4
来 自:南京师范大学
等 级:贵宾
威 望:12
帖 子:4496
专家分:141
注 册:2006-12-28
收藏
得分:0 
分析下代码
// yanzi.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
int isOneSolve(int n, int nMax = 100)
{
    int nRet=0; //记录满足本条件数
    for (int nd=2; nd*nd<n; ++nd)
    {
        if ( n%nd==0 && n/nd<nMax)
            if (++nRet>1) return 0;
    }
    return nRet;
}
//条件1,sum的任意和的分拆之积不可能有唯一分解,否则对方可能猜出
//庞涓的条件我们首先可以分析,就是他手里和可以看出,孙的手里面不是素数之积(同时不是素数3次方),可以和合数积或合数与素数之积。同时拆分的组合小于100的组合不小于2.。也就是说把孙手里面的积分解不能是唯一的结果
//燕子在这里处理这个条件。
int p1(int sum)
{
    if(sum<6) return 0;
    for (int t=(sum-1)/2; t>1; --t)
    {
        if ( isOneSolve(t*(sum-t)) ) return 0;//一旦出现只能分解一次的情况肯定是不满足条件了了。。
    }
    return 1;
}
//条件2,只有一种积的分拆满足条件1
int p2(int times2)
{
    int nRet=0; //记录满足本条件数
    for (int nd=2; nd*nd<times2; ++nd)
    {
        if ( times2%nd==0 && p1(nd+times2/nd) )//这里判断是,孙的条件,孙说开始不知道,现在知道了。。
                                              //那么孙的积肯定满足庞涓的条件了,他就用庞涓的条件来检验,同时还要唯一解
                                              //否则他是没办法得到结果的
            if (++nRet>1) return 0;
    }
    return nRet;
}
//条件3,只有一种和的分拆满足条件2
int p3(int sum)
{
    int nRet=0; //记录满足本条件数
    for (int t=(sum-1)/2; t>1; --t)//孙知道条件了,庞就从孙的思路下推了,只要判断手里面的符合孙的条件就可以了。。同时还要满足唯一性。。否则他还是不知道
    {
        if ( p2(t*(sum-t)) )
            if (++nRet>1) return 0;
    }
    return nRet;
}

int _tmain(int argc, _TCHAR* argv[])
{
    for (int n=6; n<200; ++n) // 从庞涓的条件开始入手,穷举和的可能,最大不超过200
    {
        // 其和能同时满足条件1和3者即为结果
        if (p1(n) && p3(n))
        {
            //找出对应解
            for (int t=2; t*2<n; t++)//这里可以用的是庞的条件,合数积或合数与素数之积t+=2;
            {
                if ( p2(t*(n-t)) ) //分拆结果符合条件2就输出
                    printf("%d %d\n", t, n-t);
            }
        }
    }
    puts("Finish");getchar();return 0;
    return 0;
}

[[it] 本帖最后由 sunkaidong 于 2008-6-12 23:06 编辑 [/it]]

[[it] 本帖最后由 sunkaidong 于 2008-6-13 10:16 编辑 [/it]]

[[it] 本帖最后由 sunkaidong 于 2008-6-13 10:20 编辑 [/it]]

学习需要安静。。海盗要重新来过。。
2008-06-12 23:05
ningyusui
Rank: 1
等 级:新手上路
帖 子:39
专家分:0
注 册:2008-1-14
收藏
得分:0 
在原贴发了个, 到这里再发一个...这是我写的垃圾代码,勉强可以解出此题
//鬼谷子问徒
//孙膑,庞涓都是鬼谷子的徒弟,一天鬼谷子出了这道题目:他从2到99选出两个不同的整数,把积告诉孙,把和告诉庞.庞说:我虽不能确定这两个数是什么,但是我肯定你也不知道这两个数是什么.孙说:我本来的确不知道,但是听你这么一说,我现在能确定这两个数字了.庞说既然你这么说,我现在也知道这两个数字是什么了.请问这两个数字是什么?为什么?
#include <set>
#include <map>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

//设此二数为x, y, 和为sum=x+y, 积为prod
const int N=100;
map<int, set<int>> products;
bool IsPrime(int n)
{
    for (int i=2; i*i<=n; i++)
        if (n%i==0) return 0;
    return 1;
}
//sum是否满足命题1: 知道和sum, 对于任意使x+y==sum的x,y, x*y的因数分解(分解为2~99之间的整数之积)方法数>1
bool IsFirst(int sum)
{
    int prod,cnt=0;
    //将sum分解为x+y, 其中, 2<=x<=sum/2, sum/2<=y<=sum-2,
    for (int x=2,y; x<=sum/2; x++)
    {
        y=sum-x;
        if (y >=100) continue;//y显然是<=99
        prod=x*y;
        //如果x与y均是质数, 显然分解方法只有一个...
        if (IsPrime(x)&&IsPrime(y)) return 0;
        //计算分解方法数
        cnt=0;
        for (int t=2; t*t<prod; t++)
            if (prod%t==0 &&prod/t<N )
                cnt++;
        if (cnt<2) return 0;
    }
    return 1;
}
//根据第一句话, 我们得出了sum及prod的可能值集products
//对于每个sum, 有一个相应的prod集
//同样, 孙也得出了products集合, 但他从此集合中推断出了x,y
//也就是推断出了sum, 而我们推断不出来, 是因为他知道x*y的值prod
//所以, prod在集合products中出现并且只出现一次,所以在products中
//同时属于两个sum的prod值要从相应集合中排除.
void Second()
{
    map<int,set<int>>::iterator it=products.begin();
    map<int, int> prodCnt;
    for (; it!=products.end(); it++)
    {
        for (set<int>::iterator it_set=it->second.begin();
            it_set!=it->second.end(); it_set++)
            prodCnt[*it_set]++;
    }
    for (it=products.begin(); it!=products.end(); it++)
    {
        for (set<int>::iterator it_set=it->second.begin();
            it_set!=it->second.end(); )
        {
            if (prodCnt[*it_set]>1) it_set=it->second.erase(it_set);
            else    it_set++;
        }
    }
}
//
void Third()
{
    map<int,set<int>>::iterator it=products.begin();
    for (;it!=products.end(); it++)
        if (it->second.size()==1)
        {
            int sum=it->first, prod=*(it->second.begin()), x=(sum-(int)sqrt(sum*sum-4*prod+0.1))/2;
            printf("解为: sum=%d\tprod=%d\n即x=%d\ty=%d\n", sum, prod, x, sum -x);
        }
}

void Display()
{
    printf("当前的嫌疑解(状态表)为:\n");
    puts("和sum的取值: \t乘积prod的取值:");
    map<int,set<int>>::iterator it=products.begin();
    for (; it!=products.end(); it++)
    {
        printf("%d:   ", it->first);
        for(set<int>::iterator it_set=it->second.begin(); it_set!=it->second.end(); it_set++)
            printf(" %d", *it_set);
        puts("\n");
    }
}
int main ()
{
    //对sum进行枚举,暴力啊暴力...我喜欢暴力
    //和sum取值范围为2+3到98+99 其实sum的上界可以优化到55...
    for (int sum=5, cnt=0; sum<=197; sum++)
    {
        if (IsFirst(sum))
        {
            for (int i=2; i<=sum/2; i++)
                products[sum].insert(i*(sum-i));
        }
    }

    //输出当前解的可能范围:
    Display();

    //处理第二句话
    Second();
    //输出当前解的可能范围:
    Display();
    Third();
    return 0;
}
2008-06-13 01:01
ningyusui
Rank: 1
等 级:新手上路
帖 子:39
专家分:0
注 册:2008-1-14
收藏
得分:0 
把IsPrime函数删除之, 没用的东西, 提高不了效率,倒增长了我的代码.
//鬼谷子问徒
//孙膑,庞涓都是鬼谷子的徒弟,一天鬼谷子出了这道题目:他从2到99选出两个不同的整数,把积告诉孙,把和告诉庞.庞说:我虽不能确定这两个数是什么,但是我肯定你也不知道这两个数是什么.孙说:我本来的确不知道,但是听你这么一说,我现在能确定这两个数字了.庞说既然你这么说,我现在也知道这两个数字是什么了.请问这两个数字是什么?为什么?
#include <set>
#include <map>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

//设此二数为x, y, 和为sum=x+y, 积为prod
const int N=100;
map<int, set<int>> products;
//sum是否满足命题1: 知道和sum, 对于任意使x+y==sum的x,y, x*y的因数分解(分解为2~99之间的整数之积)方法数>1
bool IsFirst(int sum)
{
    int prod,cnt=0;
    //将sum分解为x+y, 其中, 2<=x<=sum/2, sum/2<=y<=sum-2,
    for (int x=2,y; x<=sum/2; x++)
    {
        y=sum-x;
        if (y >=100) continue;//y显然是<=99
        prod=x*y;
        //计算分解方法数
        cnt=0;
        for (int t=2; t*t<prod; t++)
            if (prod%t==0 &&prod/t<N )
                cnt++;
        if (cnt<2) return 0;
    }
    return 1;
}
//根据第一句话, 我们得出了sum及prod的可能值集products
//对于每个sum, 有一个相应的prod集
//同样, 孙也得出了products集合, 但他从此集合中推断出了x,y
//也就是推断出了sum, 而我们推断不出来, 是因为他知道x*y的值prod
//所以, prod在集合products中出现并且只出现一次,所以在products中
//同时属于两个sum的prod值要从相应集合中排除.
void Second()
{
    map<int,set<int>>::iterator it=products.begin();
    map<int, int> prodCnt;
    for (; it!=products.end(); it++)
    {
        for (set<int>::iterator it_set=it->second.begin();
            it_set!=it->second.end(); it_set++)
            prodCnt[*it_set]++;
    }
    for (it=products.begin(); it!=products.end(); it++)
    {
        for (set<int>::iterator it_set=it->second.begin();
            it_set!=it->second.end(); )
        {
            if (prodCnt[*it_set]>1) it_set=it->second.erase(it_set);
            else    it_set++;
        }
    }
}
//
void Third()
{
    map<int,set<int>>::iterator it=products.begin();
    for (;it!=products.end(); it++)
        if (it->second.size()==1)
        {
            int sum=it->first, prod=*(it->second.begin()), x=(sum-(int)sqrt(sum*sum-4*prod+0.1))/2;
            printf("解为: sum=%d\tprod=%d\n即x=%d\ty=%d\n", sum, prod, x, sum -x);
        }
}

void Display()
{
    printf("当前的嫌疑解(状态表)为:\n");
    puts("和sum的取值: \t乘积prod的取值:");
    map<int,set<int>>::iterator it=products.begin();
    for (; it!=products.end(); it++)
    {
        printf("%d:   ", it->first);
        for(set<int>::iterator it_set=it->second.begin(); it_set!=it->second.end(); it_set++)
            printf(" %d", *it_set);
        puts("\n");
    }
}
int main ()
{
    //对sum进行枚举,暴力啊暴力...我喜欢暴力
    //和sum取值范围为2+3到98+99 其实sum的上界可以优化到55,
    //并且可以只取奇数...不过,对计算机来说,这么点差距,需要考虑么?
    for (int sum=5, cnt=0; sum<=197; sum++)
    {
        if (IsFirst(sum))
        {
            for (int i=2; i<=sum/2; i++)
                products[sum].insert(i*(sum-i));
        }
    }

    //输出当前解的可能范围:
    Display();

    //处理第二句话
    Second();
    //输出当前解的可能范围:
    Display();
    Third();
    return 0;
}
2008-06-13 01:09
ningyusui
Rank: 1
等 级:新手上路
帖 子:39
专家分:0
注 册:2008-1-14
收藏
得分:0 
庞在听过孙的话后, 可以得出下表:
当前的嫌疑解(状态表)为:
和sum的取值:    乘积prod的取值:
11:    18 24 28

17:    52

23:    76 112 130

27:    50 92 110 140 152 162 170 176 182

29:    54 100 138 154 168 190 198 204 208

35:    96 124 174 216 234 250 276 294 304 306

37:    160 186 232 252 270 336 340

41:    114 148 238 288 310 348 364 378 390 400 408 414 418

47:    172 246 280 370 442 480 496 510 522 532 540 550 552

53:    240 282 360 430 492 520 570 592 612 630 646 660 672 682 690 696 700 702
可是, 庞从此表中能够知道x,y的值, 说明什么?庞只比我们多知道一个sum的值,
他就确定了x,y了, 所以, sum对应的prod值只能有一个...所以sum只能是17, 如果
sum可以是29, 那么,他怎么知道了乘积是208而不是204?
如果我们不知道sum的值, 仅根据此表是无法确定x,y的值的, 如果我是庞涓, 师父如果告诉我sum=29,我是无法确定x,y的值的, 当且仅当师父告诉我sum=17时, 我们就可以推出乘积prod=52, 然后算出x,y...

如果我认为全天下人都错了, 只有我是正确的, 恐怕, 大家会笑话我吧?所以,simpley同学还可以再仔细思考下.
2008-06-13 01:24
ningyusui
Rank: 1
等 级:新手上路
帖 子:39
专家分:0
注 册:2008-1-14
收藏
得分:0 
话说,我前面的代码居然只能用vc编译...发个新的..支持mingw的
//鬼谷子问徒
//孙膑,庞涓都是鬼谷子的徒弟,一天鬼谷子出了这道题目:他从2到99选出两个不同的整数,把积告诉孙,把和告诉庞.庞说:我虽不能确定这两个数是什么,但是我肯定你也不知道这两个数是什么.孙说:我本来的确不知道,但是听你这么一说,我现在能确定这两个数字了.庞说既然你这么说,我现在也知道这两个数字是什么了.请问这两个数字是什么?为什么?
#include <set>
#include <map>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

//设此二数为x, y, 和为sum=x+y, 积为prod
const int N=100;
map<int, set<int> > products;
//sum是否满足命题1: 知道和sum, 对于任意使x+y==sum的x,y, x*y的因数分解(分解为2~99之间的整数之积)方法数>1
bool IsFirst(int sum)
{
    int prod,cnt=0;
    //将sum分解为x+y, 其中, 2<=x<=sum/2, sum/2<=y<=sum-2,
    for (int x=2,y; x<=sum/2; x++)
    {
        y=sum-x;
        if (y >=100) continue;//y显然是<=99
        prod=x*y;
        //计算分解方法数
        cnt=0;
        for (int t=2; t*t<prod; t++)
            if (prod%t==0 &&prod/t<N )
                cnt++;
        if (cnt<2) return 0;
    }
    return 1;
}
//根据第一句话, 我们得出了sum及prod的可能值集products
//对于每个sum, 有一个相应的prod集
//同样, 孙也得出了products集合, 但他从此集合中推断出了x,y
//也就是推断出了sum, 而我们推断不出来, 是因为他知道x*y的值prod
//所以, prod在集合products中出现并且只出现一次,所以在products中
//同时属于两个sum的prod值要从相应集合中排除.
void Second()
{
    map<int,set<int> >::iterator it=products.begin();
    map<int, int> prodCnt;
    for (; it!=products.end(); it++)
    {
        for (set<int>::iterator it_set=it->second.begin();
            it_set!=it->second.end(); it_set++)
            prodCnt[*it_set]++;
    }
    for (it=products.begin(); it!=products.end(); it++)
    {
        for (set<int>::iterator it_set=it->second.begin();
            it_set!=it->second.end(); )
        {
            if (prodCnt[*it_set] > 1)
            {
                set<int>::iterator itmp=it_set++;
                (it->second.erase(itmp));
            }
            else    it_set++;
        }
    }
}
//
void Third()
{
    map<int,set<int> >::iterator it=products.begin();
    for (;it!=products.end(); it++)
        if (it->second.size()==1)
        {
            int sum=it->first, prod=*(it->second.begin()), x=(sum-(int)sqrt(sum*sum-4*prod+0.1))/2;
            printf("解为: sum=%d\tprod=%d\n即x=%d\ty=%d\n", sum, prod, x, sum -x);
        }
}

void Display()
{
    printf("当前的嫌疑解(状态表)为:\n");
    puts("和sum的取值: \t乘积prod的取值:");
    map<int,set<int> >::iterator it=products.begin();
    for (; it!=products.end(); it++)
    {
        printf("%d:   ", it->first);
        for(set<int>::iterator it_set=it->second.begin(); it_set!=it->second.end(); it_set++)
            printf(" %d", *it_set);
        puts("\n");
    }
}
int main ()
{
    //对sum进行枚举,暴力啊暴力...我喜欢暴力
    //和sum取值范围为2+3到98+99 其实sum的上界可以优化到55,
    //并且可以只取奇数...不过,对计算机来说,这么点差距,需要考虑么?
    for (int sum=5, cnt=0; sum<=197; sum++)
    {
        if (IsFirst(sum))
        {
            for (int i=2; i<=sum/2; i++)
                products[sum].insert(i*(sum-i));
        }
    }

    //输出当前解的可能范围:
    Display();

    //处理第二句话
    Second();
    //输出当前解的可能范围:
    Display();
    Third();
    return 0;
}
2008-06-13 01:37
StarWing83
Rank: 8Rank: 8
来 自:仙女座大星云
等 级:贵宾
威 望:19
帖 子:3951
专家分:748
注 册:2007-11-16
收藏
得分:0 
赞飞燕,代码清晰。
最近有点忙,先收藏下,以后来看
学习了~~~

专心编程………
飞燕算法初级群:3996098
我的Blog
2008-06-13 01:41
快速回复:关于鬼谷子问徒
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.014132 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved