劃分問題(dp)

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

 

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