> 文章列表 > 【Java版oj】day29求正数数组的最小不可组成和、有假币

【Java版oj】day29求正数数组的最小不可组成和、有假币

【Java版oj】day29求正数数组的最小不可组成和、有假币

目录

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

(1)原题再现

(2)问题分析

(3)完整代码

 二、有假币

(1)原题再现

(2)问题分析

(3)完整代码


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

(1)原题再现

求正数数组的最小不可组成和_百度笔试题_牛客网
        给定一个全是正数的数组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的最小不可组成和。

(2)问题分析

      其实这就是一道找寻所有组合可能的情况的题。

      例如数组{3,2,4},可能的集合为{3},{2},{4},{3,2},{3,4},{2,4},{3,2,4}(不包含空集)。得到的和经过排序为2,3,4,5,6,7,9。8不在连续的当中,所以输出8。

       这里我其实利用了动态规划的思想,但是没有用状态方程求解,而是遍历寻找 。我们知道Set集合有去重的功能,所有我们就用这个保存所有组合情况的和。首先第一步先将0 添加进集合,每次将数组中一个数与集合中的每一个数进行求和运算,并把结果放在队列中。还以数组{3,2,4}为例,第一个数3与0进行运算,此时队列中就有3。队列不为空,删去队列中的数并将其放入集合中。这时,集合里就有0,3。其次第二步将数组中下一个数与集合中的每一个数进行求和运算,2+0=2,2+3=5,加入队列,删除队列中元素加入集合。接着第三步将数组中下一个数与集合中的每一个数进行求和运算,4+0=0,4+3=7,4+2=6,4+5=9。最后数组遍历完毕,删去s集合中一开始加入的0。

        大家可能疑惑的点,如果数组里有0,set又有去重功能,最后再把0删去不就少了一种情况嘛?这种可能是不可能的,因为题目要求全是正数。

        第二要额外定义一个链表,不能直接加入集合吗,当然不行,set集合的循环在内层,如果每次都直接加入进去,set集合的长度就不断的在改变。造成死循环。

(3)完整代码

import java.util.*;/** 求正数数组的最小不可组成和*/
public class Solution {public int getFirstUnFormedNum(int[] arr) {Set <Integer> set = new HashSet<>();Deque <Integer>queue = new LinkedList<>();int len = arr.length;set.add(0);for (int i = 0; i < len; i++) {for (int j : set) {queue.offer(arr[i] + j);}while (!queue.isEmpty()) {set.add(queue.poll());}}set.remove(0);Object[]setArr = set.toArray();Arrays.sort(setArr);int min = (int)setArr[0];int max = (int)setArr[setArr.length - 1];for (int k = min; k <= max; k++) {if (!set.contains(k)) {return k;}}return max + 1;}
}

 二、有假币

(1)原题再现

有假币__牛客网

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

 

输入描述:

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

 

输出描述:

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

示例1

输入

3

12

0

输出

1

3

(2)问题分析

        是很经典的一道题目。不是用二分法,而是每次都将硬币分成3份。我感觉第一次很难有人直接想出来要分三次。

对于 1个硬币,称量 0次。
对于 2个硬币,称量 1次。
对于 3个硬币,称量 1次。
对于 4个硬币,称量 2次,先分成(2,2,0),第一次称量前两份(2,2),如果重量不一样,再次求出判断另外2个硬币需要称量的次数。
对于 5个硬币,称量 2次,先分成(2,2,1),第一次称量前两份(2,2),如果重量不一样,再次判断另外1个硬币需要称量的次数。
对于 6个硬币,称量 2次,先分成(2,2,2),第一次称量前两份(2,2),如果重量不一样,再次判断求出另外2个硬币需要称量的次数。
对于 7个硬币,称量 2次,先分成(3,3,1),第一次称量前两份(3,3),如果重量不一样,再次判断求出另外3个硬币需要称量的次数。
        每次都将称量的硬币分成3份,要求前两份的个数不小于第三份。如果前两份重量是一样,则假币在第三份中,这样可以除去2/3的硬币;如果前两份重量不一样,那么假币在重量轻的一份中,这样也可以除去2/3的硬币。

(3)完整代码

import java.util.Scanner;
import java.util.zip.CheckedOutputStream;/** 有假币*/
public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);while (sc.hasNextInt()) {int n = sc.nextInt();if (n == 0) {return ;}System.out.println(fun(n));}}public static int fun(int n) {int count = 0;if (n == 1) {return 0;}if (n <= 3) {return 1;}int part = (n + 2) / 3;int left = n - part * 2;count = Math.max(fun(part), fun(left)) + 1;return count;}
}