【备战蓝桥杯】8.砝码称重——dp与空间压缩

342 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。

请你计算一共可以称出多少种不同的正整数重量?

注意砝码可以放在天平两边。

输入格式:
输入的第一行包含一个整数 N。
第二行包含 N个整数:W1,W2,W3,⋅⋅⋅,Wn\

输出格式:
输出一个整数代表答案。\

数据范围

对于 50%的评测用例,1≤N≤15。
对于所有评测用例,1≤N≤100,N 个砝码总重不超过 1e5。

思路

老生常谈,先找规律。如果要使用动态规划做的话,我们需要一个状态转移的思路。

不难发现,每次新增一个砝码,假设之间已经有了一些砝码并且知道所有可以称出的重量,我们对于每个之前能够称出的重量分别加一次和减一次这个砝码的重量。如果得到了新的重量,就记录下来。枚举完后,就能够得到新的所有可以称出的重量。以此类推,即可dp。

由于题设所知所有砝码的重量不超过1e5,是一个不大的数字,我们便可以在dp中枚举砝码的重量。

设dp(i,j),其值代表前i个砝码能否称出j这个重量,能的话为1,不能就为0.范围:1<=i<=n ,1<=j<=1e5.这里需要注意,j不可以为0,因为0不是一个重量。或者说0不需要称出来。

那么,对于i0,我们只需要枚举dp(i0-1,j)为1的所有j,然后令dp(i0,j+w),dp(i0,j),dp(i0,abs(j-w))都为1即可。注意减法要取绝对值。为了节省空间,我们把i这个维度压缩掉,在代码中变成两个dp[1e5]一维数组,每次枚举的时候就令其一为i0-1的状态,其二为i0的状态即可。具体详见代码。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=100000+5;
int dp1[maxn];
int dp2[maxn];
int arr[105];
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>arr[i];
	}
	dp1[arr[0]]=1;
	int *dp=dp2;
	int *last=dp1;
	for(int i=1;i<n;i++){
		memcpy(dp,last,sizeof(int)*maxn);
		dp[arr[i]]=1;
		for(int j=1;j<maxn;j++){
			if(last[j]){
				dp[j+arr[i]]=1;
				dp[abs(j-arr[i])]=1;
			}
		}
		
		if(last==dp1){
			last=dp2;
			dp=dp1;
		}else{
			last=dp1;
			dp=dp2;
		}
	}
	int cnt=0;
	for(int i=1;i<maxn;i++){
		if(last[i])cnt++;
	} 
	cout<<cnt;
	return 0;
}