题意:
有一堆石头,用整数数组 stones 表示。其中 stones[ i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。
那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y- x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0 。
1 <= stones. length <= 30
1 <= stones[ i] <= 100
解法:
令d[ i] [ j] 表示前i个石头能否合并为一个大小为j的石头.
边界为d[ 0 ] [ 0 ] = 1.
转移:
设当前为d[ i] [ j] , 有一个石头p,
1. 如果j和p合并, 那么d[ i+ 1 ] [ max ( j, p) - min ( j, p) ] = 1 ;
2. 如果j和p不合并, 那么可以看作是有一个j+ p的大石头, d[ i+ 1 ] [ j+ p] = 1 ,
为什么可以看作是j+ p呢, 因为这样对之后的操作没有影响:
假设我们有石头a和b, 不合并的话就变为( a+ b) ,
如果这时候新加入一个石头c:
1. 若( a+ b) <= c, 那么显然可以操作出一个c- ( a+ b) 的石头, 不影响.
2. 若( a+ b) > c, 我们如果能操作出一个( a+ b) - c的石头, 那么就能证明不影响,
如果a>= c或b>= c, 那么就可以操作出( a- c) + b或者a+ ( b- c) , 即a+ b- c, 不影响,
如果a< c且b< c, 此时先将a, b, c变成c- a, b, 0 , 然后变成0 , b- ( c- a) , 0 , 即有了a+ b- c, 也不影响.
dp完之后从小到大枚举j, 若d[ n] [ j] = 1 , 则return j.
code:
const int maxm= 3e3 + 5 ;
int d[ 33 ] [ maxm] ;
class Solution {
public :
int lastStoneWeightII ( vector< int > & a) {
int n= a. size ( ) ;
int s= 0 ;
for ( auto i: a) s+= i;
for ( int i= 0 ; i<= n; i++ ) for ( int j= 0 ; j<= s; j++ ) d[ i] [ j] = 0 ;
d[ 0 ] [ 0 ] = 1 ;
for ( int i= 0 ; i< n; i++ ) {
int p= a[ i] ;
for ( int j= 0 ; j<= s; j++ ) {
if ( ! d[ i] [ j] ) continue ;
d[ i+ 1 ] [ j+ p] = 1 ;
d[ i+ 1 ] [ max ( p, j) - min ( p, j) ] = 1 ;
}
}
for ( int j= 0 ; j<= s; j++ ) if ( d[ n] [ j] ) return j;
return - 1 ;
}
} ;