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