算法实验8:划分问题 动态规划?

Description

给定一个正整数的集合A={a1,a2,….,an},是否可以将其分割成两个子集合,使两个子集合的数加起来的和相等。例A = { 1, 3, 8, 4, 10} 可以分割:{1, 8, 4} 及 {3, 10}

Input

第一行集合元素个数n  n <=300 第二行n个整数

Output

如果能划分成两个集合,输出任意一个子集,否则输出“no”

Sample Input

5
1 3 8 4 10

Sample Output

3 10

分析:

做题的时候首先想到的是,这道题是集合划分,划分条件是两个集合的数值相等,那么自然想到求总和,并除2,这样就可以得到集合的值;同时既然总和能够平分,那么总和必为偶数,奇数不符合题意“no”;接下来我们用一个二维矩阵进行推算,解释道理。

矩阵中,0-13代表值。如果集合中没有任何元素,即空集,也就是第0行,那么值也为0,条件成立,赋值为1,即[0][0]=1.

如果集合中有一个元素,也就是第1行,那么此时集合应该表示为[1,空集],空集永远存在,因此值为0的位置会被继承即[1][0]=1.

此时集合中的元素能够组成的值为0、1,因此[1][1]=1.(这里看不懂,没关系,再往下看)

如果集合中有两个元素,也就是第二行,那么集合应该表示为[1,3,空集]。这时所组成的数值为:0,1,3,4恰好是矩阵中第二行中1表示的位置。

如果集合中有三个元素,也就是第三行,那么集合应该表示为[1,3,8,空集]。这时所组成的数值为:0,1,3,4,8,9,11,12.

到这里我们可以停一下了,要是这样算,太麻烦了,是不是可以从矩阵中找到计算方法呢,我们都知道矩阵有一种好处就是记录过去计算的数值。说到这里,可能有人反应过来,如果有三个元素,其实有一部分情况在两个元素的情况时就已经计算过了,而两个元素时,一部分情况在有一个元素时就计算过了。突然发现这是一个递归,只需要每次需要将新出现的值与之前出现的所有情况进行值得相加。因为这一个集合,所以集合中的元素单独拿出来,也是一个值。

到这里,这个题已经过半,接下来就是如何划分集合了。

首先我们看13列,这一列代表有值13出现的集合,第四行和第五行又出现了13,究竟选哪一行的值呢?(第四行是在第三行的基础上在集合中添加了4;第五行是在第四行的基础上添加了10)在第四行时就刚好就出现了13这个值。那我们还要第五行干嘛~

因此我们记录a[4]也就是4,这时 sum=13-4=9,值变成9了,那我们就得去找第九列,按照先前的方法我们找到了a[3]=8,记录,同时9-8=1.以此类推~知道值变成0.

这时需要想到,万一整个数组都循环完了,sum 没变成0 怎么办,当然就是“no”了。

有些注意的点请通过代码进行理解,以上是思路。另外,也就是这个题数据小,开了二维数组,否则就爆了。其实开一维数组也可以,这种方法。。。。还不会,回头再说吧  =0=!!!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int dp[301][100005];
int a[301];
int ans[301];
int main()
{
    int n;
    long long int sum=0;
    cin>>n;
    memset(dp,0,sizeof(dp));
    memset(ans,0,sizeof(ans));
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    if(sum%2!=0)
    {
        cout<<"no"<<endl;
    }
    else
    {
        dp[0][0]=1;
        sum/=2;
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<=sum; j++)
            {
                if(dp[i-1][j]==1)
                {
                    dp[i][j]=1;
                    if(a[i]+j<=sum)
                        dp[i][j+a[i]]=1;
                }
            }
        }
        int flag=0;
        int s=0;
        for(int i=n; i>0; i--)
        {
            if(dp[i][sum]==1)
            {
                if(a[i]==0)
                    ans[s++]=a[i];
                if(dp[i-1][sum]==1)
                {
                    continue;
                }
                else
                {
                    sum-=a[i];
                    //cout<<"***"<<a[i]<<endl;
                    ans[s++]=a[i];
                }
            }
        }
        s--;
        if(dp[n][sum]==0)
            cout<<"no"<<endl;
        else
        {
            for(int i=s; i>=0; i--)
            {
                if(i==0)
                    cout<<ans[i];
                else
                    cout<<ans[i]<<" ";
            }
        }
        /*cout<<"\n";
        sum=13;
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=sum;j++)
                {
                    cout<<dp[i][j];
                }
                cout<<"\n";
        }*/
    }
    return 0;
}
发布了77 篇原创文章 · 获赞 7 · 访问量 9075

猜你喜欢

转载自blog.csdn.net/qq_41886231/article/details/102361772
今日推荐