增加难度的抽签问题(最内层使用二分法)

抽 签

你的朋友提议玩一个游戏:将写有数字的n 个纸片放入口袋中,你可以从口袋中抽取4 次纸

片,每次记下纸片上的数字后都将其放回口袋中。如果这4 个数字的和是m,就是你赢,否

则就是你的朋友赢。你挑战了好几回,结果一次也没赢过,于是怒而撕破口袋,取出所有纸

片,检查自己是否真的有赢的可能性。请你编写一个程序,判断当纸片上所写的数字是k1,

k2, …, kn 时,是否存在抽取4 次和为m 的方案。如果存在,输出Yes;否则,输出No。

限制条件

 1 ≤ n ≤ 50

 1 ≤ m ≤ 108

 1 ≤ ki ≤ 108

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

穷举发:

for (int a = 0; a < n; a++)

 {
for (int b = 0; b < n; b++) 

{

for (int c = 0; c < n; c++) 

{
for (int d = 0; d < n; d++)

 {
if (k[a] + k[b] + k[c] + k[d] == m)

 {
f = true;
}
}
}
}
}

上面是最初所记载的程序的循环部分。最内侧关于d的循环所做的事就是

检查是否有d使得k[a] + k[b] + k[c] + k[d] = m

通过对式子移向,就能得到另一种表达方式

检查是否有d使得  k[d] = m - k[a] - k[b] - k[c]

就是说,检查数组k中所有元素,判断是否有m - k[a] - k[b] - k[c] 这个数值的元素

让我们着眼这一点来考虑快速的检查方法。使用名为二分搜索的算法。

二分法:

记所要查找的值m - k[a] - k[b] - k[c]为x。要预先把数组k排好序,然后看k中央的数字,可知:

 如果它比x小,x只可能在它的后面半段。

 如果它比x大,x只可能在它的前面半段。

如果再将上述方法运用在已经减半的x的存在区间上,x的存在区间就变成了初始的1/4。这样反复

操作就可以不断缩小x的存在区间,最终可以确定x存在与否。




//假设我们的数据已经进行了排序(从小到大的顺序)

#include <stdio.h>


#define MAX_N 100


/*

x:要求抽签的数值

n:数据的个数

a:存放数据的数组

*/
static int search(int x, int n, int a[]);


int main(int argc, char** argv)
{


int n;
int m;
int a[MAX_N];


printf("input n:");
scanf("%d", &n);
printf("input m:");
scanf("%d", &m);


printf("input your array\n");


int i = 0;
for(i=0;i<n;i++)
{
scanf("%d", &a[i]);
}


//three loops
int j = 0;
int k = 0;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
for(k=0;k<n;k++)
{
int x = m - a[i] - a[j] - a[k];
if( search(x, n, a) )
{
printf("%d=%d+%d+%d+%d\n",m,x,a[i],a[j],a[k]);
}
}
}


}


printf("end\n");




return 0;
}


static int search(int x, int n, int a[])
{


/*

注意 l 跟 n 的取值的变化

l:判断区间的 数组的“起始下标值”

n:判断区间的数组的截止的“位置下标+1”

*/

int l = 0;
int r = n;


while(r-l>=1)//这个区间中存在数值
{
int i = (l + r)/2;

if(x == a[i])
{
printf( "yes\n" );
return 1;
}
else if(x > a[i])
{
l = i + 1;
}
else
{
r = i;//r = i-1+1;
}


}

return 0;

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值