有假币,求正数数组的最小不可组成和

有假币

题目描述:

居然有假币!
现在猪肉涨了,但是农民的工资却不见涨啊,没钱怎么买猪肉啊。nowcoder这就去买猪肉,结果找来的零钱中有假币!!!可惜nowcoder
一不小心把它混进了一堆真币里面去了。只知道假币的重量比真币的质量要轻,给你一个天平(天平两端能容纳无限个硬币),请用最快的时间把那个可恶的假币找出来。

输入描述: 1≤n≤2^30,输入0结束程序。

输出描述: 最多要称几次一定能把那个假币找出来?

题解:

要找到一堆货币中的假币,最快的方式是分3堆,使用其中两堆称重即可:
假币比真币轻,所以:
(1) 如果A<B,那么假币在A
(2)如果A>B,那么假币在B
(3) 如果A=B,那么假币在C
以上将一堆货币分为三份,是最快找到假币的方式
题目问最多需要称几次,那么最坏的情况就是假币每次都在三份最多的那份里边
对于n个货币,n的范围为[1,2^30],每次分三份,找最多的那份,即为n/3向上取整。
如n=4,则分为1, 1, 2,取最多的2 n=5,则分为2,2,1,取最多的2 .

解题思路:
在这里插入图片描述
以上需要解决n/3向上取整的问题
可以使用Math.ceil()方法
在这里插入图片描述

需要注意,n/3 是int/ int,返回值也是int,小数已经丢失了
应该使用: (double)n/3 返回-个浮点数,再作为参数传入Math.ceil()方法
即: Math.cil(double)n/3)

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan=new Scanner(System.in);
        while(scan.hasNext()){
            int n=scan.nextInt();
            if(n==0) break;
            int count=0;
            while(n>=2){
                n=(int)Math.ceil((double)n/3);
                count++;
            }
            System.out.println(count);
        }
       
    }
}

求正数数组的最小不可组成和

给定一个全是正数的数组arr,定义一下arr的最小不可组成和的概念:
1,arr的所有非空子集中,把每个子集内的所有元素加起来会出现很多的值,其中最小的记为min,最大的记为max;
2,在区间[min,max]上,如果有一些正数不可以被arr某一个子集相加得到,那么这些正数中最小的那个,就是arr的最小不可组成和;
3,在区间[min,max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最小不可组成和;
举例: arr = {3,2,5} arr的min为2,max为10,在区间[2,10]上,4是不能被任何一个子集相加得到的值中最小的,所以4是arr的最小不可组成和; arr = {3,2,4} arr的min为2,max为9,在区间[2,9]上,8是不能被任何一个子集相加得到的值中最小的,所以8是arr的最小不可组成和; arr = {3,1,2} arr的min为1,max为6,在区间[2,6]上,任何数都可以被某一个子集相加得到,所以7是arr的最小不可组成和;
请写函数返回arr的最小不可组成和。

题解:
在这里插入图片描述
要满足子集和为j, arr 数组中每个元素只有一种可能,包含在其中子集中,或不包含:
如果arr[0] - arr[i]有子集和j,如果arr[i]在某个子集中,那么可以推出以下结论:
数组arr中,对非arr[i]的其他元素所组成的子集进行求和,- -定存在j-arr[i]这个子集和

解题思路:
先把能满足的子集和找到: arr[i]=j
再递推:如果f(j-arr[i])= true,那么对arr[i]元素来说,满足f(j)= true
对于题解来说,可以让arr[i]=j时,也满足f(j-arr[i]) = true,即f(0)=true
每次只是更新f(j)的值即可。如果f(j)=true,不需要更新,如果为false,更新为f(j-arr[i]) 的值

public class Solution {
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
	public int getFirstUnFormedNum(int[] arr) {
          int min=Integer.MAX_VALUE;
          int max=0;
        for(int i=0;i<arr.length;i++){
            max+=arr[i];
            min=Math.min(min,arr[i]);
        }
        
        boolean[] result=new boolean[max+1];
       result[0]=true;
        for(int i=0;i<arr.length;i++){
            for(int j=max;j>=arr[i];j--){
               result[j]=result[j-arr[i]] || result[j];
            }
        }
        for(int i=min;i<result.length;i++){
            if(!result[i])
                return i;
        }
            return max+1;
        
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在不知道假币轻重的情况下,我们可以使用类似二分查找的思路,将硬币数组分成三份,分别比较左半边、右半边和中间的硬币数量,从而得出假币的位置。 具体实现如下: 1. 定义一个函数 findFakeCoin(coins, left, right, fakeIndex),其中coins表示硬币数组,left表示数组左边界,right表示数组右边界,fakeIndex表示假币的位置。 2. 对于每一次查找,我们可以将问题分解为三种情况: (1)如果硬币数组中只有一枚硬币,那么这枚硬币就是假币。 (2)如果硬币数组中有假币,并且假币在左半边数组中,那么假币比其他硬币轻,我们需要递归调用 findFakeCoin(coins, left, mid - 1, fakeIndex)。 (3)如果硬币数组中有假币,并且假币在右半边数组中,那么假币比其他硬币重,我们需要递归调用 findFakeCoin(coins, mid + 1, right, fakeIndex)。 3. 在递归调用中,我们需要判断假币的位置是否在当前分组中,如果是,则返回当前假币的位置。 下面是Java代码实现: ```java public int findFakeCoin(int[] coins, int left, int right, int fakeIndex) { if (left == right) { return left; } int mid = (left + right) / 2; int leftSum = 0, rightSum = 0, midSum = 0; for (int i = left; i <= mid; i++) { if (i == fakeIndex) { continue; } leftSum += coins[i]; } for (int i = mid + 1; i <= right; i++) { if (i == fakeIndex) { continue; } rightSum += coins[i]; } if (leftSum > rightSum) { return findFakeCoin(coins, left, mid - 1, fakeIndex); } else if (leftSum < rightSum) { return findFakeCoin(coins, mid + 1, right, fakeIndex); } else { for (int i = left; i <= right; i++) { if (i == fakeIndex) { continue; } midSum += coins[i]; } if (midSum == leftSum) { return fakeIndex; } else if (midSum > leftSum) { return findFakeCoin(coins, left, mid - 1, fakeIndex); } else { return findFakeCoin(coins, mid + 1, right, fakeIndex); } } } ``` 其中,coins表示硬币数组,left表示数组左边界,right表示数组右边界,fakeIndex表示假币的位置。在实现中,我们使用了递归调用来解决问题,并通过比较左半边、右半边和中间的硬币数量,从而得出假币的位置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值