1. 程式人生 > >等和的分隔子集(01揹包)

等和的分隔子集(01揹包)

曉萌希望將1到N的連續整陣列成的集合劃分成兩個子集合,且保證每個集合的數字和是相等。例如,對於N=3,對應的集合{1,2,3}能被劃分成{3} 和 {1,2}兩個子集合.

這兩個子集合中元素分別的和是相等的。

對於N=3,我們只有一種劃分方法,而對於N=7時,我們將有4種劃分的方案。

輸入包括一行,僅一個整數,表示N的值(1≤N≤39)。

輸出包括一行,僅一個整數,曉萌可以劃分對應N的集合的方案的個數。當沒發劃分時,輸出0。

樣例輸入

7

樣例輸出

4

計蒜客上面的一道題。

主要就是兩個for迴圈,第一個for迴圈,枚舉出1-n,第二個for迴圈,列舉他們的累加和,然後更新當前1-i個數累加的方案數

dp[i][j]的意思是,用前i個數累積到j,有多少種方案

dp[i][j] = dp[i-1][j]+dp[i-1][j-i];

這個轉移方程的意思是,當前的方案數等於  上一個累積到j的方案數  + 上一個累積到j-i的方案數

例如 i = 3時,首先dp[3][0] = 1,dp[3][1] = 1,  dp[3][2] = 1(這幾個都是直接複製前面的),    然後dp[3][3] = 1+1 = 2

就是本來已經湊成3的方案數 + 加入3後湊成3的方案數 = 2;

還有個問題就是 基層為1,即 dp[1][0] = 1,dp[2][1] = 1,dp[3][i] = 1,................

至於為什麼,是為了統一轉移方程,然後就需要自己去仔細想想了

以後一定回來看一遍,一定!!!

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<list>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;
ll dp[42][4500];

int main()
{
    int n;
    while( scanf("%d",&n) == 1 )
    {
        ll goal = (ll)( n*(n+1)/2);
        if( goal & 1 )    //如果和是一個奇數,則不能分成兩份
        {
            puts("0");
            continue;
        }
        goal = goal>>1;
        memset(dp,0,sizeof dp);
        dp[0][0] = 1;
        for( int i = 1 ; i <= n ; i++ )
        {
            for( int j = 0 ; j <= goal ; j++ )
            {
                if( j >= i )
                    dp[i][j] = dp[i-1][j]+dp[i-1][j-i];
                else dp[i][j] = dp[i-1][j];
            }
        }
        printf("%lld\n",dp[n][goal]/2);
    }
    return 0;
}
/*

*/

下面是進行空間優化的寫法,思路完全一樣

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<list>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;
ll dp[4500];

int main()
{
    int n;
    while( scanf("%d",&n) == 1 )
    {
        ll goal = (ll)( n*(n+1)/2);
        if( goal & 1 )
        {
            puts("0");
            continue;
        }
        goal = goal>>1;
        memset(dp,0,sizeof dp);
        dp[0] = 1;
        for( int i = 1 ; i <= n ; i++ )
        {
            for( int j = goal ; j >= i ; j-- )
                dp[j] = dp[j]+dp[j-i];
        }
        printf("%lld\n",dp[goal]/2);
    }
    return 0;
}