社会名流问题的解答----算法引论学习

算法设计思想----归纳法

1、问题描述:



社会名流是指在n个人中,一个被所有人知道但是不知道其他人的人。若用有向图描述此问题,可以表示为:若A认识B,则从A到B有一条边。问题需要从n个人中,找到社会名流。
(在图中,由于C不认识其他人,而其他人都认识C,所以C是社会名流)
同样可以推断出,在一个图中社会名流至多有一人。若多于一人,假设A与B都是社会名流,由于A是社会名流,所以B认识A,A不认识B,同理B不认识A,A认识B。矛盾。所以在一个图中,社会名流的个数为0或者1个。

2、解答:

       算法1:

           n个人(假定为1...n)。

           从第一个人开始,将第一个人<1>作为候选人,询问其他n-1个人是否都认识<1>,并且<1>不认识其他人。(此步骤询问2(n-1)个问题

           重复第二步,直到找到社会名流或所有人不是社会名流,算法结束。

算法的复杂度很容易计算,共需要2(n-1) + 2(n-1) + 2(n-1) ..... = 2(n-1) X n = 2n(n-1)个问题,复杂度为O(n2)。

       算法2:

         思想:
           使用归纳法来处理此问题,从算法1可以看出,问题主要是逐个排除候选人。当问题规模为N时,有N个候选人。所以我们将候选人的个数定义为归纳法的变量。
          我们假设问题的规模为n-1时,问题可解。基础情况为n = 1时,最多只需询问其余的n-1一个人,共2(n-1)个问题便可确定问题的解。
          问题的关键为如何将问题的规模n变为n-1。
        我们考虑任意两个人A和B,若A到B有边,则A一定不是社会名流。相反,若A到B没边,则B一定不是社会名流。由此可知,对任意两个人,只需判断A到B有没有边,便可排除一个候选人。这样,我们将问题由N变为N-1。
实现:
          将算法分为两个阶段,第一个阶段将问题的规模变为1.第二个阶段判断剩余的一个人是否是社会名流。
代码:
public int findKnown(boolean[][] relation){
		//relation代表了n个人的关系,为一个NxN的方阵
		//relation[i][j]  代表从i到j是否有边,有边为true,无边为false
		int length = relation.length;
		int i;
		//第一阶段
		int can; // 候选人
		Stack<Integer> candidate = new Stack<Integer>();
		for(i = 0; i < length ; i++){
			candidate.add(i);
		}
		can = candidate.pop();
		while(candidate.size() > 0){
			int temp = candidate.pop();
			if(relation[can][temp]){
				//从can到temp有边,删除can
				can = temp;
			}
			//无边can保持原值
		}
		//得到can,进入检测阶段
		for(i = 0 ; i < length ; i++){
			if(i == can)
				continue;
			if(relation[i][can] == true && relation[can][i] == false){
				continue;
			}else{
				return -1;
			}
		}
		return can;
	}
总结:不要总是从n-1扩展到n,有时应尝试将问题的规模降低,由n至n-1。
               
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值