算法實驗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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章