题解 | #数组分组#

数组分组

http://www.nowcoder.com/practice/9af744a3517440508dbeb297020aca86

问题 输入int型数组,询问该数组能否分成两组,使得两组中各元素加起来的和相等,并且,所有5的倍数必须在其中一个组中,所有3的倍数在另一个组中(不包括5的倍数),不是5的倍数也不是3的倍数能放在任意一组,可以将数组分为空数组,能满足以上条件,输出true;不满足时输出false

题解 分析: 将所有数组中元素和记为sum,是5的倍数的元素和记为sum5, 不是3的倍数也不是5的倍数的元素添加到数组nums中。则问题转化为问 在数组nums中是否可以选若干个元素,使得这若干个元素的和为sum/2 - sum5。若可以,输出true;否则输出false。 这么一分析,感觉有点和0-1背包问题相似 那么设dp(j,i)表示能否从nums中下标i到n-1选取若干个元素,使它们的和为j的解。有解为true,无则false。 那么就可以写出递归关系: dp(j,i) = dp(j,i+1)||dp(j-nums[i],i+1). 简单解释一下,若第i个元素不选,则dp(j,i) = dp(j,i+1);若第i个元素选取,则dp(j,i) = dp(j-nums[i],i+1); 那么边界条件就应该是: 当j=0,dp(j,i)=true; 当j=nums中下标i到n-1中任意个元素,dp(j,i)=true; 当i=n-1时,若nums[n-1]=j,则dp(j,i)=true;否则,dp(j,i)=false。

那么递归关系清楚了,就可以采取自底向上的动态规划算法或者直接递归。 先看递归:

import java.util.Scanner;
public class Maiin {
    public static void main(String[] args) {
        Scanner scn = new Scanner(System.in);
        while (scn.hasNext())
        {
            int n = scn.nextInt();
            int sum = 0;
            int sum5 = 0;
            ArrayList<Integer> nums = new ArrayList<Integer>();
            for (int i = 0; i < n; ++i) {
                int a = scn.nextInt();
                if (a % 5 == 0)
                {
                    sum5 += a;
                } else if (a % 3 != 0) {
                    nums.add(a);
                }
                sum += a;
            }
            if(sum % 2 != 0)
            {
                System.out.println("false");
            }
            else
            {
                System.out.println(dp(sum/2-sum5,0,nums));
            }
        }
    }

    public static boolean dp(int j, int i, ArrayList<Integer> nums) {
        if (j==0)
        {
            return true;
        }
        if(i == nums.size() - 1)
        {
            if (nums.get(i) == j)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        if (i >= nums.size())
        {
            return false;
        }
        return dp(j, i + 1, nums) || dp(j - nums.get(i), i + 1, nums);

    }
}

最坏情况下,时间复杂度O(2^n)

再看动态规划

#include<vector>
#include<algorithm>

using namespace std;

bool dp(vector<int> &num, int t,int k)
{
    if (t==0 && k==num.size()) return true;
    if (k==num.size()) return false;
    //如果只是3的倍数,不能加到该集合中。
    if (!(num[k]%3)) return(dp(num,t,k+1));
    if (num[k]==t) return true;
    //对于一个数有两种选择,加或者不加到该集合中
    return(dp(num,t-num[k],k+1)|dp(num,t,k+1));
}

int main()
{
    int n;
    while(cin>>n)
    {
        vector<int> num;
        int x,sum=0,part=0;//只要找到总和的一半即可,剩下的数字之和自然为总和的一半。
        for (int i=0;i<n;i++)
        {
            cin>>x;
            //如果是5的倍数,不放入数组num
            if (x%5)
                num.push_back(x);
            else
                part += x;
            sum += x;
        }
        //如果所有数之和不是偶数,则肯定是false
        if (sum%2)
        {
            cout<<"false"<<endl;
        }
        else
        {
            sum = sum/2;
            if (dp(num,sum-part,0))
                cout<<"true"<<endl;
            else cout<<"false"<<endl;
        }
        
        num.clear();
    }
}

其实还有一种取巧的方法: 根据题意直接可以知道:5的倍数的元素和 - 3的倍数的元素和 + 其他元素的加加减减 = 0。 那么将其他元素取绝对值再升序排列成数组nums,设当前值sum = 5的倍数的元素和 - 3的倍数的元素和,那么由于已经排列的元素具有单调性,从大到小依次去一个元素,根据sum的正负决定是加上还是减去该元素。最终看sum是否为0,即可。 需要注意的是,由于sum的初值和已经排列的元素之间没有单调关系,所以对于取出的第一个元素nums[n-1]不能确定是加上还是减去它,所以分开做两次同样的操作即可。 至于可以这样做的原理,应该可以通过严谨的数学推理证明,简单的说就是在于我们的目的是尽可能的对sum加上或者减去nums中的一个元素,尽可能的使sum向0靠近。 代码如下:

/*
取巧的方法
 */
import java.util.Arrays;
import java.util.Scanner;
import java.lang.Math;
public class Main {
    public static void main(String[] args) {
        Scanner scn = new Scanner(System.in);
        while (scn.hasNext())
        {
            int n = scn.nextInt();
            int[] nums = new int[n];
            int sum = 0;
            for (int i = 0; i < n; ++i) {
                int a =scn.nextInt();
                if (a %5 == 0)
                {
                    sum += a;
                }
                else  if (a %3 == 0)
                {
                    sum -= a;
                }
                else
                {
                    nums[i] = Math.abs(a);
                }
            }
            Arrays.sort(nums);
            int sum1 = sum;

            sum += nums[n - 1];
            for (int i = n - 2;i >= 0; --i)
            {
                if(nums[i] == 0)
                {
                    break;
                }
                if (sum <= 0)
                {
                    sum += nums[i];
                }
                else
                {
                    sum -= nums[i];
                }
            }

            sum1 -= nums[n - 1];
            for (int i = n - 2;i >= 0; --i)
            {
                if(nums[i] == 0)
                {
                    break;
                }
                if (sum1 <= 0)
                {
                    sum1 += nums[i];
                }
                else
                {
                    sum1 -= nums[i];
                }
            }
            System.out.println(sum * sum1 == 0 ? "true" : "false");

        }
    }
}
全部评论

相关推荐

点赞 1 评论
分享
牛客网
牛客企业服务