这道题有两种做法,先介绍第一种方法,这种方法我是把每个砝码抽象成步数,即题目问你给你n个移动的机会,每个机会有一个固定的权重wi,你可以向前走wi,也可以向后走wi,也可以不走,当然起始位置为0,且位置为-x和x是等价的,即你可以走到-x那么你也可以走到x;
思路是这样的;
用一个vector<int>存储可以走到的位置,用一个记录数组记录位置x是可以被走到,如果可以即st[x]=1,否则st[x]=0;
1:考虑第i个移动的机会,其权重为wi,且前i-1个可以移动的位置都已经计算完毕
2:利用遍历vector里的所有元素,不妨设遍历到sto[j],如果abs(sto[j]+wi])没有被走过,则st[sto[j]+wi]=1,且将sto[j]+wi插入到vector中,且对abs(sto[j]+wi])、abs(wi)进行相同的操作;
3:当以上循环结束时,vector的长度就是答案;
说实话这道题有一点像走台阶,即你可以向前走x步也可以走y步,然后问你有多少种方法可以走到一个特定的位置;
那么接下来就是代码了;
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
vector<int> sto;
int st[N];
int n;
int main(){
cin>>n;
for(int i=0;i<n;i++){
int x; cin>>x;
int mid=sto.size();
for(int j=0;j<mid;j++){
int x1=abs(x-sto[j]); int y1=abs(x+sto[j]);
if(!st[x1]&&x1!=0) st[x1]=1,sto.push_back(x1);
if(!st[y1]&&y1!=0) st[y1]=1,sto.push_back(y1);
}
if(!st[x]) st[x]=1,sto.push_back(x);
}
cout<<sto.size();
}
那么接下来就是dp的方法了,这个方法我们可以用集合的角度进行思考,这道题说白了就是一个简单的背包【本菜鸟惭愧】;
首先对每一个状态进行定义,即定义集合的含义;
1:f[i][j]为选取前j个物品前重量为i的集合是否为空;
然后就是进行想状态转移的方程了,这里考虑最后一个不同点,即考虑第j个物品,这无非有三种选择;
1:不选第j个物品,即我们看f[j][i-1]是否为空即。
2:选第j个物品,
(1)即看f[abs(wi-j)][i-1]是否为空【选后走wi步】
(2)即看f[abs(wi+j)][i-1]是否为空【wi为第i个移动的步数】【向前走wi步】
那么接下来就是代码了
#include<iostream>
#include<algorithm>
using namespace std;
const int N=110000;
int f[N][110];
int n;
int st[N];
int main(){
cin>>n;
int max=0;
for(int i=1;i<=n;i++) cin>>st[i],max+=st[i];
for(int i=0;i<=n;i++) f[0][i]=1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= max; j++) {
if (f[j][i - 1]) f[j][i] = 1;//不取
if (f[abs(j + st[i])][i - 1]) f[j][i] = 1;//取负
if (f[abs(j - st[i])][i - 1]) f[j][i] = 1;//取正
}
}
int ans=0;
for(int i=1;i<=max;i++){
if(f[i][n]) ans++;
}
cout<<ans;
}