【蓝桥杯】砝码称重

3417. 砝码称重 - AcWing题库

题意:

思路回顾:

首先这道题一开始我没想用DP做,看到标签是入门题就没想DP qwq

其实这就是一个普通背包

一开始设计状态设计不出来,刚开始设的是dp[i][j]表示前i个物品能表示j种重量

显然是不行的,答案不能写在参数里

而且这样状态转移也会很麻烦,少了a[i],上一层的状态也表示不了,因此这样设计一定是错的

后来看到小数据是15,那就暴力dfs吧

然后在写dfs的时候设计的状态设对了,(i,j)表示前i个物品表示重量为j的方案数

然后去枚举决策,因为它是一个天平,有三种决策:

放左边,放右边,不放

这样设计状态后,状态转移就容易多了

因此我们设计完状态之后,发现不好转移,大概率是状态设计错了qwq

放左就是从(i-1,j+a[i])转移过来

放右就是从(i-1,abs(j-a[i]))转移过来

不放就是从(i-1,j)转移过来

然后i>n是出口,我们用set维护重量种类数即可

这样的复杂度是3^n,显然跑不了所有,但是可以骗分

对于OI赛制的题,要按照小数据范围的提示进行一些暴力骗分qwq

那么暴力我们写出来了,再去试了DP,这次状态设计为:dp[i][j]表示前i个物品重量为j

然后状态转移方程也成功写出来了,但是我却错在答案统计上....

显然答案是从dp[n][j]转移过来的,枚举一下,放set里面维护种类数就好了

但是我却枚举了所有的状态然后统计

所以dp数组推出来了之后的答案统计也要搞清楚

好像该去学一下记忆化搜索咋写,这样只会爆搜不会DP的时候就直接写个记忆化就好了

Code:

暴力:

#include <bits/stdc++.h>
using namespace std;
const int mxn=20;
set<int> S;
int n;
int a[mxn];
void dfs(int i,int j){
	if(i>n){
		if(j!=0) S.insert(j);
		return;
	}
	dfs(i+1,j);
	dfs(i+1,j+a[i+1]);
	dfs(i+1,abs(j-a[i+1]));
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	dfs(0,0);//爆搜
	cout<<S.size()<<'\n';
}

DP:

#include <bits/stdc++.h>
using namespace std;
const int mxn=1e2+10,mxv=2e5+10;
set<int> S;
int n,sum=0;
int a[mxn],dp[mxn][mxv];
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	memset(dp,0,sizeof(dp));
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=1e5;j++){
			dp[i][j]=(dp[i-1][j]+dp[i-1][j+a[i]])+(dp[i-1][abs(j-a[i])]);
		}
	}
	for(int i=1;i<=1e5;i++){
		if(dp[n][i]) S.insert(i);
	}
	cout<<S.size()<<'\n';
}

总结:

DP:

状态设计的时候,答案不能放在参数里,它本身就是答案

状态转移的时候如果发现很难转移,基本就是状态设计错了

然后dp数组推出来了之后的答案统计也要搞清楚

其他:

对于OI赛制的题,要按照小数据范围的提示进行一些暴力骗分

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值