首页 > 试题广场 >

组合幸运数字

[编程题]组合幸运数字
  • 热度指数:1203 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
小易的幸运数字是7,现有一个整数数组 nums,请你找出并返回能被七整除的子集合的最大和,如果找不到则返回-1。

输入描述:
一个正整数数组列表nums,用空格区分,1<=length(nums)<=100000,sum(nums) <= 1000000000


输出描述:
一个整数,最大和
示例1

输入

7 3 1 4

输出

14

说明

7+3+4
示例2

输入

6 5

输出

-1

说明

找不到 一个子数组,和能被7整除
示例3

输入

10 20 2 29

输出

49

说明

20+29

这道题很难, 一开始会想到遍历得到所有的子集合的和, 但是却不知道如何遍历得到。因为这里的子集合长度可以为1 ~ nums.length,一般的遍历无法满足!

举例: 1 2 3 4

一般的遍历是 1, 1 2, 1 2 3, 1 2 3 4, 2, 2 3, 2 3 4, 3,3 4,4实现这个遍历已经是O(n^2)的复杂度了! 可是这还没结束, 还需要 1 3, 1 3 4, 1 4, 2 4, 这种循环遍历极其难写出来!!

🌟那我们这里想到了一个巧妙地方法, 逆向思维, 由于全部的数之和sum肯定是由所有的数组成, 那我们可以试着慢慢地扒元素下来直到把所有元素扒完,

比如说我们这里有一个数组 [a,b,c,d], 依次扒开就很容易得到所有的组合
const readline=require('readline');
const rl=readline.createInterface({
    input:process.stdin,
    output:process.stdout
});
/*
 小易的幸运数字是7,现有一个整数数组 nums,
 请你找出并返回能被七整除的子集合的最大和,如果找不到则返回-1
 输入:
 一个正整数数组列表nums,用空格区分,1<=length(nums)<=100000,sum(nums) <= 1000000000
 */
rl.on('line',function (line) {
    /*1.处理数据*/
    let arr=line.trim().split(' ').map(Number);

    /*2.dp*/
    //由于是全是正整数,所以 sum >= arr.length
    let sum=arr.reduce((a,b)=>a+b);
    //dp[i]记录i是否能够得到 1为可得到,0则相反
    let dp=new Array(sum+1).fill(0);
    dp[0]=1; dp[sum]=1;
    /*这个方法很巧妙, 最初是a+b+c+d,每次从所有的满足条件的子集合减去一个数
    * 这样就很方便的得到了所有的排列*/
    for (let i=0;i<arr.length;i++){
        for (let j=0;j<=sum;j++){
            if (dp[j]===1&&j>=arr[i]){
                dp[j-arr[i]]=1;
            }
        }
    }

    let maxSum=-1;
    for (let i=dp.length-1;i>=7;i--){
        if (dp[i]===1&& i%7===0){
            maxSum=i;
            break;
        }
    }
    console.log(maxSum);
})




编辑于 2021-03-27 22:13:33 回复(1)
抄小冬瓜的,稍微改了点优化了下
#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int main()
{
    vector<int> nums;
    int t;
    while(cin>>t)    {nums.push_back(t);}
    sort(nums.begin(), nums.end());
    int sum =0;
    for(auto i:nums)    {sum +=i;}
    if(sum%7==0)    {cout<<sum<<endl;return 0;}
    vector<int> te;te.push_back(sum);
    for(int i =0;i<nums.size();i++)
    {
        int size_te = te.size();
        for(int j =0;j<size_te;j++)
        {
            int temp = te[j] -nums[i];
            if(temp!=0&&temp%7==0)     {cout<<temp<<endl;return 0;}
            else {te.push_back(temp);}
        }
    }
    
    cout<<"-1"<<endl;
    return 0;
}


发表于 2021-01-19 10:08:32 回复(0)
同样可以先求解背包问题,判断哪些和能够凑出来,然后降序遍历输出能被7整除的最大的那个和,跟2021网易另一道题“小易的考试成绩”如出一辙
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] strArr = br.readLine().split(" ");
        int[] arr = new int[strArr.length];
        int sum = 0;
        for(int i = 0; i < strArr.length; i++) {
            arr[i] = Integer.parseInt(strArr[i]);
            sum += arr[i];
        }
        // 求解背包问题
        int[] dp = new int[sum + 1];        // dp[i]用来记录i是否能得到
        dp[0] = 1;
        dp[sum] = 1;
        for(int i = 0; i < arr.length; i++){
            dp[i] = 1;
            for(int j = 0; j <= sum; j++)
                if(dp[j] == 1 && j >= arr[i]) dp[j - arr[i]] = 1;
        }
        // 降序遍历能取到的总和,输出最大的
        for(int lucky = sum; lucky >= 0; lucky--){
            if(dp[lucky] == 1 && lucky % 7 == 0){
                System.out.println(lucky);
                break;
            }
        }
    }
}


编辑于 2021-03-12 17:51:59 回复(5)

lyst = list(map(int, input().split()))
# 数组所有数字之和
total = 0
for i in lyst:
    total += i
# 数组排序
lyst.sort()
res = [total]
flag = False
for i in lyst:
    # 注意此处不能直接用res循环,否则后面append之后res长度增加还会继续循环内层
    res_set = set(res)
    for j in res_set:
        temp = j - i
        if temp % 7 == 0:
            flag = True
            print(temp)
            break
        else:
            res.append(temp)
    if flag: break
if not flag:
    print(-1)

发表于 2021-01-01 11:43:53 回复(2)
#include<bits/stdc++.h>
using namespace std;
int main()
{
vector<int> data;
int num;
while(cin>>num)
data.push_back(num);
vector<int> dp(7,0); //dp[i] 除以7余i的累加和
for (int &a : data)
for (int &i : vector<int>(dp))
dp[(i + a) % 7] = max(dp[(i + a) % 7], i + a);//不用和用
cout<< dp[0];
return 0;
}

编辑于 2021-04-06 22:25:20 回复(2)
dp, O(n)复杂度
import sys
from math import inf
l = [int(s) for s in input().split()]
l.sort()
n = len(l)
prev = [0] * 7
prev[l[0] % 7] = l[0]
for i in range(1, n):
    dp = prev.copy()
    cur = l[i] % 7
    dp[cur] = max(prev[cur], l[i])
    for j in range(7):
        if prev[j] != 0:
            idx = (j + l[i]) % 7
            dp[idx] = max(prev[idx], prev[j] + l[i])
    prev = dp
print(prev[0])


发表于 2023-08-26 17:32:29 回复(0)
动态规划算法。所有数进行求和后进行减法,找出0-6每个取余值的最小减数。算法时间复杂度O(7*n)
#include<iostream>
#include<vector>
using namespace::std;

int main()
{
    vector<int> nums; 
    int x;
    long sum;
    do{
        cin>>x;
        nums.push_back(x);
        sum+=x;
    }while(cin.get()!='\n');
     vector<vector<int>> dp(nums.size()+1,vector<int>(7,sum));
    dp[0][sum%7]=0;
    for(int i=1;i<nums.size()+1;i++)
    {
        int x=nums[i-1];
        for(int j=0;j<7;j++)
        {
            dp[i][j]=min(dp[i-1][j],dp[i-1][(x%7+j)%7]+x);
        }
    }
    cout<<sum-dp.back()[0]<<endl;
}


发表于 2022-04-11 12:58:52 回复(2)
ll dp1[8] = {0};
ll dp2[8] = {0};
vector<ll> a;
int main()
{
    int t;
    while( cin>>t)
    {
        a.push_back(t);
    }
    for(ll now:a)
    {
        for(int i=0;i<7;i++){
            dp2[i]=dp1[i];
            dp1[i]=0;
        }
        for(int i=0; i<7; i++)
        {
            if(dp2[i]||i==0)
            {
                dp1[(i+now)%7]=max(dp2[i]+now,dp2[(i+now)%7]);
            }
        }
        for(int i=0;i<7;i++){
            dp1[i]=max(dp1[i],dp2[i]);
        }
    }
    if(dp1[0]==0)dp1[0]=-1;
    writeln(dp1[0]);
}

发表于 2022-03-26 14:31:59 回复(0)
//暴力搜索
#include <bits/stdc++.h>
using namespace std;

int ret = 0;
void backTracking(const vector<int>& nums,int sum, int index)
{
    if(index >= nums.size())
    {
        if(sum % 7 == 0)
            ret = max(ret, sum);
        return;
    }
    for(int i = index;i < nums.size();i++)
    {
        sum += nums[i];
        backTracking(nums, sum, i+1);
        sum -= nums[i];
    }
}
int main()
{
    vector<int> nums;
    int num;
    while(cin >> num)
        nums.push_back(num);
    backTracking(nums, 0, 0);
    cout << ret << endl;
    return 0;
}

发表于 2022-03-25 20:44:14 回复(1)
只要先求出总和,然后计算要剪减掉的余数即可,寻找要减掉的余数可以用二进制回溯的方式从小到大对一个升序数组进行搜索。
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    vector<int> nums;
    int max_sum = -1;
    int sum = 0;
    while(scanf("%d", &n) != EOF)
    {
        sum += n;
        int rest = n % 7;
        // 加入所有7的整数倍
        if(rest != 0)
        {
            nums.push_back(n);
        }
    }
    int rest = sum % 7;
    // 刚好是7的倍数
    if(rest == 0)
    {
        cout << sum << endl;
        return 0;
    }

    //每组都排序
    sort(nums.begin(), nums.end());
    int tmp_sum = 0;
    function <void(int)> dfs = 
        [&](int deps)
    {
        if(deps == -1)
        {
            return ;
        }
        // 不加入直接判断
        if(tmp_sum % 7 == rest && tmp_sum != sum)
        {
            max_sum = sum - tmp_sum;
            return ;
        }
        dfs(deps - 1);
        // 加入
        tmp_sum += nums[deps];
        if(tmp_sum % 7 == rest && tmp_sum != sum)
        {
            max_sum = sum - tmp_sum;
            return ;
        }
        dfs(deps - 1);
        //还原
        tmp_sum -= nums[deps];
        return ;
    };

    for(int i = 0; i < nums.size(); i++)
    {
        tmp_sum = 0;
        dfs(i);
        if(max_sum != -1)
        {
            break;
        }
    }
    cout << max_sum << endl;
    return 0;
}


发表于 2022-03-20 19:03:12 回复(0)
用一个set记录下所有的子集的和,在求子集和的同时判断是否满足能被7整除且和最大,
这里要注意的是对set进行遍历的同时添加元素会存在并发问题,因此借用了一个临时set2来解决

import java.io.*;
import java.util.*;
public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] ss = reader.readLine().split(" ");
        Set<Integer> set=new HashSet();
        set.add(0);
        Integer ans=Integer.MIN_VALUE;
        for(String s:ss ){
            Iterator<Integer> it=set.iterator();
            Set<Integer> set2=new HashSet();
            while(it.hasNext()){
                Integer t=it.next();
                Integer v=Integer.parseInt(s)+t;
                set2.add(t);
                set2.add(v);
                if(v%7==0 && v>ans){
                    ans=v;
                }
            }
            set=set2;
        }
        if(ans==Integer.MIN_VALUE){
            System.out.println(-1);
        }
        else{
            System.out.println(ans);
        }
    }
}


编辑于 2021-04-05 16:48:14 回复(1)
import java.io.*;

public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] s = reader.readLine().split(" ");
        int sum  = 0;
        int[] arr = new int[s.length];
        for(int i = 0 ; i < s.length ;i++){
           arr[i] = Integer.parseInt(s[i]);
            sum += arr[i];
        }
        int[] dp = new int[sum+1];
         dp[sum] = 1; dp[0] = 1;
        for(int i = 0 ; i < arr.length;i++){
            for(int j = 0; j <= sum;j++)
              if(dp[j] == 1 && j >= arr[i]) dp[j -  arr[i]] = 1;
        }
        for(int lucky = sum; lucky >= 0; lucky--){
            if(dp[lucky] == 1 && lucky % 7 == 0){
                System.out.println(lucky);
                break;
            }
        }
        
    }
}

发表于 2021-03-12 13:30:12 回复(0)