寻找水王·扩展

        题目:随着Tango的发展,管理员发现,“超级水王”没有了。统计结果表明,有3个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4。你能从发帖ID列表中快速找出他们的ID吗?

      分析:编美里面寻找水王的方法已经说明白了,遍历一遍,每次减少两个元素,将原问题分解成更小规模的问题,这其中的原因是因为减少了问题规模之后原来“水王帖子的数量超过总数的一半”的特性依然存在。扩展的问题思路也是一样,根据题意,水王们的发帖数目一定会超过1/4,因此在后面写代码的时候不考虑找出水王之后验证合不合法。参考原问题的解法,可以知道:如果每次删除四个不同的ID,则在剩下的ID列表中,原先发帖比例大于1/4的ID所占比例仍然大于1/4。不断重复这个过程,最终得到答案。

      网上有很多直接在原问题的解法基础上,暴力遍历候选集candidates。在原问题的时候candidates只有一个,使用一个变量来保存就可以,在拓展问题中candidates有三个,因此需要用一个长度为3的数组来保存,另外用于计数candidates出现次数的标志也从原来的一个变量变成一个长度为3的数组。之所以说这种解法暴力,是大家的代码因为有很多if-else,把各种情况考虑周全了。这里转一份网上大神们的参考代码(转载地址,侵删。)

void Find(Type* ID, int N,Type candidate[3])
{
    Type ID_NULL;//定义一个不存在的ID
    int nTimes[3], i;
    nTimes[0]=nTimes[1]=nTimes[2]=0;
    candidate[0]=candidate[1]=candidate[2]=ID_NULL;
    for(i = 0; i < N; i++)
    {
        if(ID[i]==candidate[0])
        {
             nTimes[0]++;
        }
        else if(ID[i]==candidate[1])
        {
             nTimes[1]++;
        }
        else if(ID[i]==candidate[2])
        {
             nTimes[2]++;
        }
        else if(nTimes[0]==0)
        {
             nTimes[0]=1;
             candidate[0]=ID[i];
        }
        else if(nTimes[1]==0)
        {
             nTimes[1]=1;
             candidate[1]=ID[i];
        }
        else if(nTimes[2]==0)
        {
             nTimes[2]=1;
             candidate[2]=ID[i];
        }
        else
        {
             nTimes[0]--;
             nTimes[1]--;
             nTimes[2]--;
         }
    }
    return;
}

        但是,我寻思着,如果不止3个水王呢?如果是4个水王,5个水王,甚至是更多水王?事实上1个水王的时候水贴超过一半,3个水王的时候每人水贴超过1/4,可以推出有n个水王,每人所占水贴超过1/(n+1),因此应该有个通用的思路来找出这些水王。下面是我自己实现,遍历帖子的每一个元素时:1)首先检查candidates中有没有出现过,如果出现过,将对应的计数器加1;2.1)如果全部没有出现过,则找到第一个计数器为0的位置,作为这个candidate的的位置,并把计数器加1;2.2)如果这些candidates的计数器全部不为0,以为着当前元素跟这n个水王候选都不一样,因此删去这(n+1)个人。相应的代码如下,为了便于测试,假设原数组中都是整数,因此可以用负数来初始化候选人数组。

void findKnums(int data[], int size) {
	if (data == NULL || size <= 0) return;

	int counts[NUM] = { 0 };
	int results[NUM] = { -1 };

	for (int i = 0; i < size; ++i) {
		bool isInK = false;
		bool hasZeroCount = false;
		for (int j = 0; j < NUM; ++j) {
			if (results[j] == data[i]) {
				counts[j]++;
				isInK = true;
				break;
			}
		}
		if (!isInK) {
			for (int j = 0; j < NUM; ++j) {
				if (0 == counts[j]) {
					results[j] = data[i];
					counts[j]++;
					hasZeroCount = true;
					break;
				}
			}
			if (!hasZeroCount) {
				for (int j = 0; j < NUM; ++j)
					counts[j]--;
			}
		}
	}

	for (int j = 0; j < NUM; ++j)
		cout << results[j] << " ";
	cout << endl;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值