砝码称重
好歹想到动态规划!!!
该有这样动态规划的思想,并不一定需要自己考虑周全,要让程序递推
前i个数是否能产生j这个数值
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int N=105;
int a[N];
int n;
int dp[N][100005];
//该有这样动态规划的思想,并不一定需要自己考虑周全,要让程序递推
//前i个数是否能产生j这个数值
signed main() {
cin>>n;
int sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
//dp[1][a[1]]=1;
//for(int i=1;i<=n;i++)dp[i][a[i]]=1;
//其实提前多初始化一点绝对不是坏事,防止漏掉某条状态转移代码
for(int i=1;i<=n;i++){
for(int j=1;j<=sum;j++){
dp[i][j]|=dp[i-1][j];
dp[i][a[i]]=1;//不要漏了这一条,除了前i-1个递推来的,
//加上的第i个也可以搞出一个新状态 ,当然可以提前初始化这一点
dp[i][j+a[i]]|=dp[i-1][j];//累加总是可以的
//在dp[i-1][j]的若干种情况下加入第i个砝码,能重新得到哪些
if(a[i]>j)dp[i][a[i]-j]|=dp[i-1][j];
if(a[i]<j)dp[i][j-a[i]]|=dp[i-1][j];
}
}
/*
dp[1][a[1]]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=sum;j++){
dp[i][j]=dp[i-1][j];//前i个发麻,j是否能从某些状态得到,非常明了
//在dp[i-1][j]的若干种情况下加入第i个砝码,能重新得到哪些
if(a[i]>j)dp[i][j]|=dp[i-1][a[i]-j];
if(a[i]<j)dp[i][j]|=dp[i-1][j-a[i]];
if(a[i]==j)dp[i][j]=1;
}
}
统一一下形式,由于会递推到每一个i,j
每次只考虑dp[i][j]由哪些状态转化而来就ok
不要直接看由 dp[i-1][j]能得到哪些状态,
虽然后者也可以,因为到i时,i-1的所有情况dp[i-1][j]
一定已经提前算出来了(相当于上一行的所有值已经推出来了)
1\注意状态只有0,1两种情况的,最好加上'|=',可以由众多状态推来
但有些状态推不出,but在他之前有状态能推到,不能再被赋值为0了
2\不管是考虑 未知状态可以由哪些状态推来 还是
已知状态可以推出哪些状态,有点很重要,一定要考虑全面,不然很可能结果就为0
*/
set<int> S;//其实大可不必,下面i就是不同的呀
S.clear();
for(int i=1;i<=sum;i++) {
if(dp[n][i])S.insert(i);
}
cout<<S.size();
return 0;
}