题解/算法 {2835. 使子序列的和等于目标的最少操作次数}
LINK: https://leetcode.cn/problems/minimum-operations-to-form-subsequence-with-target-sum/description/
;
贪心题…
将T按照二进制拆分为[t1 < t2 < ... < tt]
, 从小到大遍历ti
:
.
@IF(ti
等于
A
A
A里若干个数的和): 将
A
A
A里的这些数删除掉, 对答案不产生代价; (这是个贪心策略, 即从最小的
t
i
ti
ti开始 而不是从大的);
.
@ELSE: 必须从
A
A
A里找一个
>
t
i
>ti
>ti的数
a
a
a 然后对他进行分解, 这里非常非常重要 怎么对
a
a
a进行分解呢? 比如ti = 2, a = 16
, 将他分解为[8, 4, 2, 2]
对答案的贡献是
3
3
3(16->8->4->2
), 因为一个非常重要的性质 下一个ti
是严格底层的 即t[i+1] > ti
, 即所有的ti
都是不相同的, 因此 你不需要将他全部变成[2,2,2,2,...]
这样的话 会超时的 (比如ti=2, a = (1<<30)
);
int minOperations(vector<int>& A, int T){
unordered_map< int, int> Mp; for( auto i : A) Mp[ i] ++;
auto TryTo_eliminate = [&]( int _tar){
auto Back = Mp;
int t = _tar;
while( true){
while( _tar > 0 && Mp[ t] > 0){ -- Mp[ t], _tar -= t;}
if( _tar == 0) break;
if( t == 1) break;
t /= 2;
}
if( _tar == 0){ return true;}
Mp = move( Back);
return false;
};
int ANS = 0;
for( int i = 0; i <= 30; ++i){
if( 0 == ((T >> i) & 1)){ continue;}
int tar = (1 << i);
if( TryTo_eliminate( tar)){ continue;}
int cont = 0;
bool succ = false;
for( long long t = tar * 2LL; t <= (1LL << 30); t *= 2){
++ cont;
if( Mp[ t] > 0){
succ = true;
Mp[ t] --;
for( long long j = tar; j < t; j *= 2) Mp[ j] ++;
ANS += cont;
break;
}
}
if( false == succ) return -1;
}
return ANS;
}