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;
}