观察题目样例解释:
可以将物品放在两边的天平中,可以将物品的重量看作+w
、-w
。
把物品分配在天平两边问题可以转换成从背包总容量为m
的背包中选取重量为+w
、-w
的物品的方案数。
集合:所有从前i
件物品中选择且重量为j
的所有方案的集合
属性:集合是否非空(boolean值)
将集合划分为3类
情况1:不选当前第i
件物品
情况2: 选当前第i
件物品且为 +w
情况3:选当前第i
件物品且为 -w
由于可以选择-w
的情况,数组下标会出现负数情况。
所以,我们需要给下标加上一个偏移量B
,使数组下标不越界。
import java.util.*;
public class Main{
static int N=110,M=200010;
static int a[]=new int[N];
static boolean f[][]=new boolean[N][M];
static int B=M/2;
//考虑到边界,数组下标不能是负数
//这里加上偏移量,将数组下标变为合法值
public static void main(String []args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=0;
for(int i=1;i<=n;i++) {
a[i]=sc.nextInt();
m+=a[i];
}
f[0][B]=true;
//0件物品一件都不装是一种方案
//记为true
for(int i=1;i<=n;i++) {
//
for(int j=-m;j<=m;j++) {
f[i][j+B]=f[i-1][j+B];
//从前i-1个物品中选,不选第i个物品
if(j-a[i]>=-m)f[i][j+B]|=f[i-1][j-a[i]+B];
//从前i-1个物品中选,且选了第i个物品+a[i]
if(j+a[i]<=m)f[i][j+B]|=f[i-1][j+a[i]+B];
//从前i-1个物品中选,且选了第i个物品-a[i]
//考虑到数组下标越界,数组下标不能是负数。
//需要加上偏移量B
}
}
int res=0;
for(int j=1;j<=m;j++) {
if(f[n][j+B])res++;
//j+B是因为砝码总重为1e5
//负数情况:-1e5+B>=0
//负数情况也被包含进来,所以直接对f[n][j+B]判断即可。
//检测所有从1-i个数中体积为j的集合是否非空
//非空则加上1
}
System.out.println(res);
}
}