题目大意:
有n(n<=100)个石头,每次我们可以选择2个石头让其碰撞,产生的新石头的重量为abs(x-y),其中x,y为原来的两个石头的重量,问我们怎么选取石头才能让最后的石头重量最轻。石头重量和<= 1e5
解题思路:
显然,这题需要DP来解决,但是我们乍看之下很难确定这个dp的状态,题目其实数据范围给了一定的提示,我们的状态最好设定为这样dp[n][weight],n是个数,weight是重量。同时,我们这里有一个重要的发现,最后的石头可以看作是由两堆石头的重量差决定的。因为它们的石头碰撞的过程,无非就是相当于给每个石头前面加一个正负号。然后让我们计算最后的和。到这里,我们就明白了,其实我们只需要用dp维护其中一个堆的重量即可(另一个堆的重量可以由重量和减去本个堆的重量得到),看这个堆的重量是否可行。
dp[n][weight]表示第n个石头重量为weight是否可行
其中的arr代表原来的石头重量的数组。另外,由于这题内存比较小,保险起见还是用滚动数组。
这题通过数学运算转化为01背包,我觉得是很巧妙的思想。
#include<bits/stdc++.h>
const int MAXN=1e5+10;
using namespace std;
int main(){
int n;cin>>n;
vector<int> arrmv(n);
int sum=0;
for(int i=0;i<n;i++){cin>>arrmv[i];sum+=arrmv[i];}
vector<vector<int>> dp(2,vector<int>(MAXN,0));
int cur=0;
dp[cur][0]=1;
for(int i=0;i<n;i++){
cur = !cur;
for(int j=0;j<MAXN;j++){
dp[cur][j]= dp[!cur][j];
if(j>=arrmv[i])dp[cur][j]|=dp[!cur][j-arrmv[i]];
}
// cerr<<dp[cur][2]<<endl;
}
int ans=1e9;
for(int i=0;i<MAXN;i++)if(dp[cur][i])ans=min(ans,abs(sum-i - i));
cout<<ans<<endl;
return 0;
}