3417.砝码称重
最首先,天平用砝码称出来的质量就是通过天平两边砝码的质量差
dp
为了避免考虑不周导致遗漏,需要使得递推式保持dp[i][j]由i-1
哪些状态推导而来
假设dp[i][j]
由dp[i-1][x]
推到而来,根据不同途径,反推x的值
依次考虑由简单到复杂的情况,
1、不放 dp[i][j]|=dp[i-1][j]
2、放在重的一边,假设之前是dp[i-1][x]
,放在重的一边自然是x+a[i]=j
反推得知,x=j-a[i]
, dp[i][j]|=dp[i-1][j-a[i]]
3、放在轻的一边 假设之前是dp[i-1][x]
,即重的一边与轻的一边的差值为x
当a[i]<x
,那么重的一边还是更重,x-a[i]=j,x=a[i]+j;
当a[i]>x
,那么原来重的一边会变成轻的一边,a[i]-x=j,x=a[i]-j;
思路就没这么清晰过嘿嘿嘿
dp[i][j]|=dp[i-1][j];
if(j>=a[i])dp[i][j]|=dp[i-1][j-a[i]];//放在重的一边,判断是为了防止下标越界
//放在轻的一边,判断是为了防止下标越界
if(j<=a[i])dp[i][j]|=dp[i-1][a[i]-j];
dp[i][j]|=dp[i-1][a[i]+j];
- 总重M数组下标得开两倍,不然就会出错,
弄清楚为什么了?谨记:考虑下标越界时不要仅仅考虑越下界,数组上界也要考虑
不过数组大小只开 1e5
if(a[i]+j<=sum)dp[i][j]|=dp[i-1][a[i]+j];
想要不考虑上界,就把数组大小开到两倍
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=105;
const int M=2e5+5;
int a[N];
int dp[N][M];
int n;
signed main(){
cin>>n;
int sum=0;
for(int i=1;i<=n;i++)cin>>a[i],sum+=a[i];
dp[0][0]=1;//记得初始化
for(int i=1;i<=n;i++){
for(int j=0;j<=sum;j++){
dp[i][j]|=dp[i-1][j];
if(j>=a[i])dp[i][j]|=dp[i-1][j-a[i]];//放在重的一边,判断是为了防止下标越界
//放在轻的一边,判断是为了防止下标越界
if(j<=a[i])dp[i][j]|=dp[i-1][a[i]-j];
dp[i][j]|=dp[i-1][a[i]+j];
//以上几种情况可以概况总结为
// dp[i][j]=dp[i-1][j]|dp[i-1][a[i]+j]|dp[i-1][abs(a[i]-j)];
}
}
int res=0;
//正整数重量,不要考虑0噢
for(int i=1;i<=sum;i++)if(dp[n][i])res++;
cout<<res;
return 0;
}
暴力做法(记忆化搜索)
//每个砝码有三种情况,放右边+,放中间,放左边-;
//一共n个砝码,搜索遍历每一种情况;
//把搜到的情况记录下来
//用bool记录每一种重量,如果可以就是true
//由于存在重量存在负数,给每个重量记录时加上一个偏移量B;也可以用绝对值abs
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 110,M=200010,B=M/2;
//st数组用来储存遍历过的情况,如果搜过了这个点,就不继续dfs了,节省时间。
//记忆化搜索。
bool st[N][M];
bool f[M];
int n;
int a[N];
void dfs(int u,int sum)
{
if(st[u][sum]) return ; //已经遍历过了,就直接return;
st[u][sum]=true;//记录遍历的点
f[sum]=true;
if(u>n) return;
dfs(u+1,sum+a[u]);//放右边+
dfs(u+1,sum);//放中间
dfs(u+1,abs(sum-a[u]));//放左边-
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
dfs(1,0);
int ans=0;
for(int i=1;i<=M;i++) if(f[i]) ans++; //这里枚举要从1开始,能不能从0开始;
cout<<ans;
return 0;
}
作者:XSS
链接:https://www.acwing.com/solution/content/108799/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
bitset做法
#include<bits/stdc++.h>
using namespace std;
const int N=1e2+7,M=1e5+7;
bitset<M> S;//压位高精
int s[N];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>s[i];
S[0]=1;
for(int i=0;i<n;i++)
S|=S<<s[i];//先对组成的和进行合并,在对差值进行合并
for(int i=0;i<n;i++)
S|=S>>s[i];
cout<<S.count()-1;//减去0
}