【算法刷题日记之本手篇】求正数数组的最小不可组成和与有假币

发布于:2022-12-05 ⋅ 阅读:(407) ⋅ 点赞:(0)

⭐️前面的话⭐️

本篇文章介绍来自牛客试题广场的两道题题解,分别为【求正数数组的最小不可组成和】和【有假币】,展示语言java。

小贴士:本专栏所有题目来自牛客->面试刷题必用工具

📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2022年9月28日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《算法》,📚《算法导论》
💬推荐在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!



注意事项:本专栏所有题目都来自牛客网,如果有小伙伴还没有注册牛客,可以点击下方链接进行注册,注册完就能立即刷题了。不仅是刷题,上面还有很多有关就业的面经,面试题库,以及名企的模拟面试,我非常推荐它,博主自己用的也很多,也刷了不少题了!下图可以作证:
1

注册地址:牛客网

1

有关任何问题都可以与博主交流,你可以在评论区留言,也可以私信我,更可以加上博主的vx与博主一对一交流(文章最下方有)。

封面区


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

🔐题目详情

给定一个全是正数的数组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的最小不可组成和。

链接:求正数数组的最小不可组成和

💡解题思路

基本思路: 动态规划
解题思路:

题目让我们得到数组arr的最小不组成和,对于最小不组成和,题目给出的条件如下:

  • 1,arr的所有非空子集中,把每个子集内的所有元素加起来会出现很多的值,其中最小的记为min,最大的记为max;
  • 2,在区间[min,max]上,如果有一些正数不可以被arr某一个子集相加得到,那么这些正数中最小的那个,就是arr的最小不可组成和;
  • 3,在区间[min,max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最小不可组成和。

解决这道题的基本思路为:

第一步,求区间的最小子集和min(其实就是数组中最小的元素)和最大子集和max(其实就是数组中所有元素的和)。
第二步,遍历[min, max],判断区间内是否存在不可组成的和,找到第一个不可组成和就是最小的最小不可组成和。
第三步,对于判断某个数k是否是arr的子集和,本质上就是在数组arr中选择若干个数,看这若干个数的和是否能凑成k,那这个问题就是0-1背包问题,问题到这里转换成【在数组中选择若干个数,判断这个数是否恰好为k】。

对于【在数组中选择若干个数,判断这个数是否恰好为k】问题,我们考虑动态规划:

状态定义: 不妨定义 f [ i ] [ j ] f[i][j] f[i][j]表示从前 i i i个元素中选择若干个数,其和是否恰好为 j j j

确定初始状态: j = 0 j=0 j=0时,不论 i i i为多少,不选择元素都可以使其和恰好为 0 0 0,即 f [ i ] [ 0 ] = t r u e f[i][0]=true f[i][0]=true

状态转移方程: 对于数组中的第 i i i个元素 v a l val val,我们可以选择也可以不选择,若选择 f [ i ] [ j ] = f [ i − 1 ] [ j − v a l ] , j > = v a l f[i][j]=f[i-1][j-val], j >= val f[i][j]=f[i1][jval],j>=val,若不选择, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j],两种选择中只要任意一个为 t r u e true true,则 f [ i ] [ j ] = t r u e f[i][j]=true f[i][j]=true

综合起来, f [ i ] [ j ] = f [ i − 1 ] [ j ] ∣ ∣ f [ i − 1 ] [ j − v a l ] f[i][j]=f[i-1][j] || f[i-1][j-val] f[i][j]=f[i1][j]∣∣f[i1][jval]

优化:

我们发现 f [ i ] [ j ] f[i][j] f[i][j]依赖于 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j] f [ i − 1 ] [ j − v a l ] f[i-1][j-val] f[i1][jval],就是依赖于上一行在 j j j列前面的数,根据这个特点我们可以直接优化为一个一维数组,我们从 k k k的位置开始从后往前更新 f [ i ] [ j ] f[i][j] f[i][j]
优化

优化后的数组设为 f [ j ] f[j] f[j]表示数组和是否能够恰好凑成 j j j,根据优化思路不难得到新的状态转移方程为 f [ j ] = f [ j ] ∣ ∣ f [ j − v a l ] f[j]=f[j]||f[j-val] f[j]=f[j]∣∣f[jval],需要从右往左遍历数组才行。

初始状态 f [ 0 ] = t r u e f[0]=true f[0]=true

🔑源代码

import java.util.*;
public class Solution {
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
	public int getFirstUnFormedNum(int[] arr) {
        int min = arr[0];
        int max = arr[0];
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            if (arr[i] < min) min = arr[i];
            max += arr[i];
        }
        //判断是否为最小组成和
        int ans = max + 1;
        for (int i = min + 1; i <= max; i++) {
            if (!isSum(arr, i)) {
                ans = i;
                break;
            }
        }
        return ans;
	}
    private static boolean isSum(int[] arr, int k) {
        //状态定义
        int n = arr.length;
        boolean[] dp = new boolean[k + 1];
        //确定初始状态
        dp[0] = true;
        for (int i = 0; i < n; i++) {
            int val = arr[i];
            for (int j = k; j >= val; j--) {
                dp[j] = dp[j] || dp[j - val];
            }
        }
        return dp[k];
    }
} 

🌱总结

本题为动态规划0-1背包问题,考察状态抽象,状态方程的推导。

⭐️有假币⭐️

🔐题目详情

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

输入描述:

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

输出描述:

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

示例1

输入

3
12
0

输出

1
3

链接:有假币
来源:牛客网

💡解题思路

基本思路: 数学推理题

解题思路:
先简单说一下题目的要求,根据输入的正整数n,表示一共有n枚货币,其中有一枚是假的,题目说假币的重量比真币的重量要轻,并且给我们一个天平,求最快找出假币的最大称量次数。

我们能使用的工具是天平,最快的方案就是先尽可能找出两堆相等货币进行称量,那么至少的将货币分为3堆,并且分为3堆也是最快的,不妨设货币的总数为n,那么有以下几种情况:

  • n % 3 == 0,这种情况货币可以均分,为 n 3 , n 3 , n 3 \frac{n}{3}, \frac{n}{3}, \frac{n}{3} 3n,3n,3n,第一次天平称量之后,若前两堆的重量不同,那假币就在较轻重量的一堆里,此时剩余货币为 n 3 \frac{n}{3} 3n,若前两堆重量相同,那就是假币一定就在第三堆,此时剩余的货币也是 n 3 \frac{n}{3} 3n,然后我们再将这 n 3 \frac{n}{3} 3n个货币以同样的方式分为3组,继续以相同的规则比较,换句话说称一次可以排除 2 / 3 2/3 2/3的货币。
  • n%3==1,这种情况分组为 n 3 , n 3 , n 3 + 1 \frac{n}{3}, \frac{n}{3}, \frac{n}{3}+1 3n,3n,3n+1,同理前两组重量不相同,剩余的货币为 n 3 \frac{n}{3} 3n,相同剩余的货币为 n 3 + 1 \frac{n}{3}+1 3n+1,我们按照最坏情况考虑,此时剩余的货币为 n 3 + 1 \frac{n}{3}+1 3n+1
  • n%3==2,这种情况分组为 n 3 + 1 , n 3 + 1 , n 3 \frac{n}{3}+1, \frac{n}{3}+1, \frac{n}{3} 3n+1,3n+1,3n,同理前两组重量不相同,剩余的货币为 n 3 + 1 \frac{n}{3} + 1 3n+1,相同剩余的货币为 n 3 \frac{n}{3} 3n,我们按照最坏情况考虑,此时剩余的货币为 n 3 + 1 \frac{n}{3}+1 3n+1

综上所述,如果 n n n能够被 3 3 3整除,计数器加 1 1 1 n n n更新为 n 3 \frac{n}{3} 3n,继续同样的操作,如果 n n n能够被 3 3 3整除,计数器加 1 1 1 n n n更新为 n 3 + 1 \frac{n}{3} + 1 3n+1,继续同样的操作。

🔑源代码

Java代码实现:

// write your code here
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int n = sc.nextInt();
            if (n == 0) break;
            int ans = 0;
            while (n > 1) {
                ans++;
                n = n / 3 + (n % 3 == 0 ? 0 : 1);
            }
            System.out.println(ans);
        }
    }
}

C++版本代码:

// write your code here cpp

#include <iostream>
using namespace std;

int main() {
    int n;
    while (cin >> n) {
        if (n == 0) break;
        int ans = 0;
        while (n > 1) {
            ans++;
            n = n / 3 + (n % 3 == 0 ? 0 : 1);
        }
        cout << ans << endl;
    }
}

🌱总结

本题为数学推理题,考察数学逻辑思维能力和数学归纳能力。


到文章最后,再来安利一下吧,博主也是经常使用,并且也经常在牛客上刷题,题库也非常丰富:牛客网,刷题,面试,内推都有。也欢迎与博主交流有关刷题,技术方面,以及与博主聊聊天,交个朋友也好啊,毕竟有朋自远方来!
后记区

觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!

1-99

本文含有隐藏内容,请 开通VIP 后查看