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
思路:定义一个c[i][j]二维数组,如果 c[i][j]为真则表示{a1,a2,……a[i]}存在子集和为j.j=0时为真。例如上面的样例,写出来就是
i | i | j0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
1 | 1 | T | T | ||||||||||||
3 | 2 | T | T | T | T | ||||||||||
8 | 3 | T | T | T | T | T | T | T | T | ||||||
4 | 4 | T | T | T | T | T | T | T | T | T | T | T | |||
10 | 5 | T | T | T | T | T | T | T | T | T | T | T | T |
发现当上一个为真时,其下面一个一定为真,而且加上这个值 a[i]也一定为真。即if(c[i-1][j]==1) c[i][j+a[i]]=1,c[i][j]=1;
那写完这个表格怎么找到这个序列呢,我们知道c[i][j]为真可能从两个方向中来,c[i-1][j]和c[i-1][j-a[i]],所以当c[i-1][j]为假时我们就输出a[i]即可。具体看代码。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define LL long long
bool c[350][100100];
int a[350];
int main()
{
int n,sum=0;
cin>>n;
memset(c,0,sizeof(c));
for(int i=0; i<=n; i++)//将j==0赋为真
c[i][0]=1;
for(int i=1; i<=n; i++)
{
cin>>a[i];
sum+=a[i];
}
if(sum%2!=0)//不能分成两半就直接输出no
cout<<"no"<<endl;
else
{
for(int i=1; i<=n; i++)
for(int j=0; j<=sum/2; j++)
if(c[i-1][j]==1)
c[i][j+a[i]]=1,c[i][j]=1;//根据退出来的公式
if(c[n][sum/2]==0)//不存在这样的结构就输出no
cout<<"no"<<endl;
else{
int s=sum/2,e=0;
for(int i=n;i>=1;i--){
if(c[i][s]==1&&c[i-1][s]!=1){
if(e==0)
cout<<a[i];
else
cout<<" "<<a[i];
e++;
s-=a[i];//将和减去刚刚输出的
}
}
cout<<endl;
}
}
return 0;
}