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