(1)暴力搜索+回溯解法:(时间复杂度过高,超时)
class Solution {
public:
int ans=100;
int lastStoneWeightII(vector<int>& stones) {
shatter(stones);
return ans;
}
void shatter(vector<int>& stones){
if(stones.empty()){
ans=0;return;
}
else if(stones.size()==1){
if(stones[0]<ans)
ans=stones[0];
return;
}
for(int i=0;i<(stones.size()-1);i++){
int iNum=stones[i];
for(int j=i+1;j<stones.size();j++){
int jNum=stones[j];
//从数组中粉碎 i j石头
if(stones[i]==stones[j]){
stones.erase(stones.begin()+j);
stones.erase(stones.begin()+i);
}else if(stones[i]<stones[j]){
stones[j]=jNum-iNum;
stones.erase(stones.begin()+i);
}else{
stones[i]=iNum-jNum;
stones.erase(stones.begin()+j);
}
//对新数组进行处理
shatter(stones);
//回溯 把 i j个元素放回去
if(iNum==jNum){
stones.insert(stones.begin()+i,iNum);
stones.insert(stones.begin()+j,jNum);
}else if(iNum<jNum){
stones.insert(stones.begin()+i,iNum);
stones[j]=jNum;
}else{
stones[i]=iNum;
stones.insert(stones.begin()+j,jNum);
}
}
}
}
};
(2)动态规划法
可以深入理解一下,如果能把所有石头分为大小最接近的两堆、且,则取到的Min值为问题的解。假设,有且。这样就把问题转化成求解在石头中选取总重量<=且最接近值的背包问题。对于每个石头,有选入背包和不选(入)两种操作。
假设solution[i][j][s]为从第i到第j个石头中得到总重量为s的背包问题是否有解,最开始i=0,j=n,s=0。随着求解问题的逐步深入我们可知:
(1)当石头j+1的重量>s时,不可选它入背包中,即
(2)否则,可考虑把j+1块石头选入背包,但是需要考虑,选它的前提是能否在前面的石头中选到总重量为的石头。即:
i.如有,则可以选择石头j+1。
=
ii.否则,
iii.在实际写代码时,可以对上面的逻辑进行适当的简化,已知solution[][][]是个bool值,则(2)可合并为(把i设为固定值0)
最开始j=0,s=0且s<=,solution[0][0]=true。当solution[j][s]=true且s为接近的最大值时,。
代码为:
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
int sum = accumulate(stones.begin(), stones.end(), 0);
int n = stones.size(), m = sum /2;
vector<vector<int>> dp(n + 1, vector<int>(m+1));
dp[0][0]=true;
for(int j=0;j<n;++j){
for(int s=0;s<=m;++s){
if(stones[j]>s){
dp[j+1][s]=dp[j][s];
}else{
dp[j+1][s]=dp[j][s]||dp[j][s-stones[j]];
}
}
}
for(int j=m;;j--){
if(dp[n][j])
return sum - 2 * j;
}
}
};