试题 算法训练 无聊的逗 C语言

问题描述

  逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中。不过他想到了一个游戏来使他更无聊。他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的情况下长度最长是多少。

输入格式

  第一行一个数n,表示n个棍子。第二行n个数,每个数表示一根棍子的长度。

输出格式

  一个数,最大的长度。

样例输入

4
1 2 3 1

样例输出

3

本题算法思路:

参考资料:http://t.csdnimg.cn/dXxTI

1,用二进制表示选与不选,选表示“1” ,不选表示 “0”
2,假如选了第一根和第三根表示为:0101(从右到左),剩下没选木棍:1111 - 0101 = 1010(第一和第四)
3,假如选了第2根木棍表示为 1 << (a - 1) ,即为1 << (2 - 1),就是把二进制的1向左移动一位(0010)
4,假如1000,选择不包含第二根木棍表示为 1000 & 0010 == 0(在C语言中,&是按位与运算符。它对两个操作数的每一位进行与操作,只有当两个操作数的对应位都为1时,结果的对应位才为1,否则为0。

        1000

&      0010

 --------------

        0000

5,如果两次序列选择木棍重复,解决方法:将第1序列选择的木棍拿走 即
将“可供选择的木棍列表”和“当前枚举的木棍序列2”进行按位与:例如
00111拿的为1,2,3木棍,11100拿的是3,4,5木棍,重复的木棍为3即为
11001 & 11100 = 11000 .

完整代码:

#include<stdio.h>
int main(){
	int i,j,k;
	int n;//木棍的数量 
	scanf("%d",&n);
	int num[20];//每根木棍长度
	int maxLength = -1;//最长相同木棍的长度
	int length[1 << 20];//存储排列组合下的长度
	for(i = 0; i < n ; i++){
		scanf("%d",&num[i]);
		length[1 << i] = num[i];//将每根木棍的长度存储到排列组合中 
	}
	//遍历每一种排列组合方式
	for(i = 0; i < (1 << n); i++){
		//当前选择的木棍 情况 
		for(j = 0;j < n;j++){
			//没有选择第j根木棍 
			if((i & (1 << j)) == 0){
				continue;
			}
		//如果选择了木棍j,则当前情况的长度为选择了木棍j的情况的长度加上木棍j的长度 
		length[i] = length[i - (1 << j)] + num[j];
		break;
	} 
}
	for(i = 0; i < (1 << n); i++){
		j = (1 << n) - 1 - i;//表示还没取的木棍 即为 j为选择了所有i没有选择的木棍的情况 
		
    //k = (k - 1) & j去掉了最右边的1的所有可能情况。
		for(k = j ;k > 0; k = (k - 1) & j){
			if(length[k] == length[i]){
					maxLength = maxLength > length[k] ? maxLength : length[k];
				} 
		}	
	}
	printf("%d",maxLength);
	return 0;
}

解释以上部分代码:

1,二进制取反。例如,当n = 3时,假设i = 3(二进制数0011),则(1 << n) - 1 - i的结果为        4(二进制数0100),表示选择了第3根木棍,而没有选择第1和第2根木棍。
j = (1 << n) - 1 - i;
2,假如初始时k的值为12(二进制数1100),然后减去1得到11(二进制数1011),再与j进行按位与操作,得到新的k的值为8(二进制数1000)。这表示在选择了第3和第4根木棍的情况下,去掉了最右边的1,只选择了第3根木棍。通过不断筛选和 i 作比较 来获取最大长度。
k = (k - 1) & j
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值