动态规划 
      
    
      
      
      
        数组 
      
    
   
  
    
      
       
     
  
  
    
      
    
    
      
       
     
  
题目描述 
给你一个 只包含正整数  的 非空  数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
 
示例 1: 
输入: nums = [1,5,11,5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11] 。 
示例 2: 
输入: nums = [1,2,3,5]
输出: false
解释: 数组不能分割成两个元素和相等的子集。
 
 
提示: 
    1 <= nums.length <= 200 
    1 <= nums[i] <= 100 
 
解法 
方法一:动态规划 
我们先计算出数组的总和 \(s\) ,如果总和是奇数,那么一定不能分割成两个和相等的子集,直接返回 \(false\) 。如果总和是偶数,我们记目标子集的和为 \(m = \frac{s}{2}\) ,那么问题就转化成了:是否存在一个子集,使得其元素的和为 \(m\) 。
我们定义 \(f[i][j]\)  表示前 \(i\)  个数中选取若干个数,使得其元素的和恰好为 \(j\) 。初始时 \(f[0][0] = true\) ,其余 \(f[i][j] = false\) 。答案为 \(f[n][m]\) 。
考虑 \(f[i][j]\) ,如果我们选取了第 \(i\)  个数 \(x\) ,那么 \(f[i][j] = f[i - 1][j - x]\) ;如果我们没有选取第 \(i\)  个数 \(x\) ,那么 \(f[i][j] = f[i - 1][j]\) 。因此状态转移方程为:
\[
f[i][j] = f[i - 1][j] \textit{ or } f[i - 1][j - x] \textit{ if } j \geq x
\]
最终答案为 \(f[n][m]\) 。
时间复杂度 \((m \times n)\) ,空间复杂度 \((m \times n)\) 。其中 \(m\)  和 \(n\)  分别为数组的总和的一半和数组的长度。
Python3 Java C++ Go TypeScript Rust JavaScript 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 class   Solution : 
    def   canPartition ( self ,  nums :  List [ int ])  ->  bool : 
        m ,  mod  =  divmod ( sum ( nums ),  2 ) 
        if  mod : 
            return  False 
        n  =  len ( nums ) 
        f  =  [[ False ]  *  ( m  +  1 )  for  _  in  range ( n  +  1 )] 
        f [ 0 ][ 0 ]  =  True 
        for  i ,  x  in  enumerate ( nums ,  1 ): 
            for  j  in  range ( m  +  1 ): 
                f [ i ][ j ]  =  f [ i  -  1 ][ j ]  or  ( j  >=  x  and  f [ i  -  1 ][ j  -  x ]) 
        return  f [ n ][ m ] 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 class  Solution   { 
     public   boolean   canPartition ( int []   nums )   { 
         // int s = Arrays.stream(nums).sum(); 
         int   s   =   0 ; 
         for   ( int   x   :   nums )   { 
             s   +=   x ; 
         } 
         if   ( s   %   2   ==   1 )   { 
             return   false ; 
         } 
         int   n   =   nums . length ; 
         int   m   =   s   >>   1 ; 
         boolean [][]   f   =   new   boolean [ n   +   1 ][ m   +   1 ] ; 
         f [ 0 ][ 0 ]   =   true ; 
         for   ( int   i   =   1 ;   i   <=   n ;   ++ i )   { 
             int   x   =   nums [ i   -   1 ] ; 
             for   ( int   j   =   0 ;   j   <=   m ;   ++ j )   { 
                 f [ i ][ j ]   =   f [ i   -   1 ][ j ]   ||   ( j   >=   x   &&   f [ i   -   1 ][ j   -   x ] ); 
             } 
         } 
         return   f [ n ][ m ] ; 
     } 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 class   Solution   { 
public : 
     bool   canPartition ( vector < int >&   nums )   { 
         int   s   =   accumulate ( nums . begin (),   nums . end (),   0 ); 
         if   ( s   %   2   ==   1 )   { 
             return   false ; 
         } 
         int   n   =   nums . size (); 
         int   m   =   s   >>   1 ; 
         bool   f [ n   +   1 ][ m   +   1 ]; 
         memset ( f ,   false ,   sizeof ( f )); 
         f [ 0 ][ 0 ]   =   true ; 
         for   ( int   i   =   1 ;   i   <=   n ;   ++ i )   { 
             int   x   =   nums [ i   -   1 ]; 
             for   ( int   j   =   0 ;   j   <=   m ;   ++ j )   { 
                 f [ i ][ j ]   =   f [ i   -   1 ][ j ]   ||   ( j   >=   x   &&   f [ i   -   1 ][ j   -   x ]); 
             } 
         } 
         return   f [ n ][ m ]; 
     } 
}; 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 func   canPartition ( nums   [] int )   bool   { 
     s   :=   0 
     for   _ ,   x   :=   range   nums   { 
         s   +=   x 
     } 
     if   s % 2   ==   1   { 
         return   false 
     } 
     n ,   m   :=   len ( nums ),   s >> 1 
     f   :=   make ([][] bool ,   n + 1 ) 
     for   i   :=   range   f   { 
         f [ i ]   =   make ([] bool ,   m + 1 ) 
     } 
     f [ 0 ][ 0 ]   =   true 
     for   i   :=   1 ;   i   <=   n ;   i ++   { 
         x   :=   nums [ i - 1 ] 
         for   j   :=   0 ;   j   <=   m ;   j ++   { 
             f [ i ][ j ]   =   f [ i - 1 ][ j ]   ||   ( j   >=   x   &&   f [ i - 1 ][ j - x ]) 
         } 
     } 
     return   f [ n ][ m ] 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 function   canPartition ( nums :   number []) :   boolean   { 
     const   s   =   nums . reduce (( a ,   b )   =>   a   +   b ,   0 ); 
     if   ( s   %   2   ===   1 )   { 
         return   false ; 
     } 
     const   n   =   nums . length ; 
     const   m   =   s   >>   1 ; 
     const   f :   boolean [][]   =   Array . from ({   length :   n   +   1   },   ()   =>   Array ( m   +   1 ). fill ( false )); 
     f [ 0 ][ 0 ]   =   true ; 
     for   ( let   i   =   1 ;   i   <=   n ;   ++ i )   { 
         const   x   =   nums [ i   -   1 ]; 
         for   ( let   j   =   0 ;   j   <=   m ;   ++ j )   { 
             f [ i ][ j ]   =   f [ i   -   1 ][ j ]   ||   ( j   >=   x   &&   f [ i   -   1 ][ j   -   x ]); 
         } 
     } 
     return   f [ n ][ m ]; 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 impl   Solution   { 
     pub   fn   can_partition ( nums :   Vec < i32 > )   ->   bool   { 
         let   s :   i32   =   nums . iter (). sum (); 
         if   s   %   2   !=   0   { 
             return   false ; 
         } 
         let   m   =   ( s   /   2 )   as   usize ; 
         let   n   =   nums . len (); 
         let   mut   f   =   vec! [ vec! [ false ;   m   +   1 ];   n   +   1 ]; 
         f [ 0 ][ 0 ]   =   true ; 
         for   i   in   1 ..= n   { 
             let   x   =   nums [ i   -   1 ]   as   usize ; 
             for   j   in   0 ..= m   { 
                 f [ i ][ j ]   =   f [ i   -   1 ][ j ]   ||   ( j   >=   x   &&   f [ i   -   1 ][ j   -   x ]); 
             } 
         } 
         f [ n ][ m ] 
     } 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 /** 
 * @param {number[]} nums 
 * @return {boolean} 
 */ 
var   canPartition   =   function   ( nums )   { 
     const   s   =   nums . reduce (( a ,   b )   =>   a   +   b ,   0 ); 
     if   ( s   %   2   ===   1 )   { 
         return   false ; 
     } 
     const   n   =   nums . length ; 
     const   m   =   s   >>   1 ; 
     const   f   =   Array . from ({   length :   n   +   1   },   ()   =>   Array ( m   +   1 ). fill ( false )); 
     f [ 0 ][ 0 ]   =   true ; 
     for   ( let   i   =   1 ;   i   <=   n ;   ++ i )   { 
         const   x   =   nums [ i   -   1 ]; 
         for   ( let   j   =   0 ;   j   <=   m ;   ++ j )   { 
             f [ i ][ j ]   =   f [ i   -   1 ][ j ]   ||   ( j   >=   x   &&   f [ i   -   1 ][ j   -   x ]); 
         } 
     } 
     return   f [ n ][ m ]; 
}; 
 
 
 
 
方法二:动态规划(空间优化) 
我们注意到,方法一中 \(f[i][j]\)  只与 \(f[i - 1][\cdot]\)  有关,因此我们可以将二维数组压缩成一维数组。
时间复杂度 \(O(n \times m)\) ,空间复杂度 \(O(m)\) 。其中 \(n\)  是数组的长度,而 \(m\)  是数组的总和的一半。
Python3 Java C++ Go TypeScript Rust JavaScript 
class   Solution : 
    def   canPartition ( self ,  nums :  List [ int ])  ->  bool : 
        m ,  mod  =  divmod ( sum ( nums ),  2 ) 
        if  mod : 
            return  False 
        f  =  [ True ]  +  [ False ]  *  m 
        for  x  in  nums : 
            for  j  in  range ( m ,  x  -  1 ,  - 1 ): 
                f [ j ]  =  f [ j ]  or  f [ j  -  x ] 
        return  f [ m ] 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 class  Solution   { 
     public   boolean   canPartition ( int []   nums )   { 
         // int s = Arrays.stream(nums).sum(); 
         int   s   =   0 ; 
         for   ( int   x   :   nums )   { 
             s   +=   x ; 
         } 
         if   ( s   %   2   ==   1 )   { 
             return   false ; 
         } 
         int   m   =   s   >>   1 ; 
         boolean []   f   =   new   boolean [ m   +   1 ] ; 
         f [ 0 ]   =   true ; 
         for   ( int   x   :   nums )   { 
             for   ( int   j   =   m ;   j   >=   x ;   -- j )   { 
                 f [ j ]   |=   f [ j   -   x ] ; 
             } 
         } 
         return   f [ m ] ; 
     } 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 class   Solution   { 
public : 
     bool   canPartition ( vector < int >&   nums )   { 
         int   s   =   accumulate ( nums . begin (),   nums . end (),   0 ); 
         if   ( s   %   2   ==   1 )   { 
             return   false ; 
         } 
         int   m   =   s   >>   1 ; 
         bool   f [ m   +   1 ]; 
         memset ( f ,   false ,   sizeof ( f )); 
         f [ 0 ]   =   true ; 
         for   ( int &   x   :   nums )   { 
             for   ( int   j   =   m ;   j   >=   x ;   -- j )   { 
                 f [ j ]   |=   f [ j   -   x ]; 
             } 
         } 
         return   f [ m ]; 
     } 
}; 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 func   canPartition ( nums   [] int )   bool   { 
     s   :=   0 
     for   _ ,   x   :=   range   nums   { 
         s   +=   x 
     } 
     if   s % 2   ==   1   { 
         return   false 
     } 
     m   :=   s   >>   1 
     f   :=   make ([] bool ,   m + 1 ) 
     f [ 0 ]   =   true 
     for   _ ,   x   :=   range   nums   { 
         for   j   :=   m ;   j   >=   x ;   j --   { 
             f [ j ]   =   f [ j ]   ||   f [ j - x ] 
         } 
     } 
     return   f [ m ] 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 function   canPartition ( nums :   number []) :   boolean   { 
     const   s   =   nums . reduce (( a ,   b )   =>   a   +   b ,   0 ); 
     if   ( s   %   2   ===   1 )   { 
         return   false ; 
     } 
     const   m   =   s   >>   1 ; 
     const   f :   boolean []   =   Array ( m   +   1 ). fill ( false ); 
     f [ 0 ]   =   true ; 
     for   ( const   x   of   nums )   { 
         for   ( let   j   =   m ;   j   >=   x ;   -- j )   { 
             f [ j ]   =   f [ j ]   ||   f [ j   -   x ]; 
         } 
     } 
     return   f [ m ]; 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 impl   Solution   { 
     pub   fn   can_partition ( nums :   Vec < i32 > )   ->   bool   { 
         let   s :   i32   =   nums . iter (). sum (); 
         if   s   %   2   !=   0   { 
             return   false ; 
         } 
         let   m   =   ( s   /   2 )   as   usize ; 
         let   mut   f   =   vec! [ false ;   m   +   1 ]; 
         f [ 0 ]   =   true ; 
         for   x   in   nums   { 
             let   x   =   x   as   usize ; 
             for   j   in   ( x ..= m ). rev ()   { 
                 f [ j ]   =   f [ j ]   ||   f [ j   -   x ]; 
             } 
         } 
         f [ m ] 
     } 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 /** 
 * @param {number[]} nums 
 * @return {boolean} 
 */ 
var   canPartition   =   function   ( nums )   { 
     const   s   =   nums . reduce (( a ,   b )   =>   a   +   b ,   0 ); 
     if   ( s   %   2   ===   1 )   { 
         return   false ; 
     } 
     const   m   =   s   >>   1 ; 
     const   f   =   Array ( m   +   1 ). fill ( false ); 
     f [ 0 ]   =   true ; 
     for   ( const   x   of   nums )   { 
         for   ( let   j   =   m ;   j   >=   x ;   -- j )   { 
             f [ j ]   =   f [ j ]   ||   f [ j   -   x ]; 
         } 
     } 
     return   f [ m ]; 
}; 
 
 
 
 
  
  
  
    
    
    
    
      
  
    
      
  
     
   
  GitHub