算法提高(VIP)——盾神与砝码称重

问题描述
有一天,他在宿舍里无意中发现了一个天平!这 个天平很奇怪,有 n 个完好的砝码,但是没有游码。

盾神为他的发现兴奋不已!于是他准备去称一称自己的东西,他准备好了 m 种物品去称。

神奇的是,盾神一早就 知道这 m 种物品的重量,他现在是想看看这个天平能不能称出这些物品出来。

但是盾神稍微想了 1 秒钟以后就觉得这个问题太无聊了,于是就丢给了你。

输入格式
第一行为两个数,n 和 m。
第二行为 n 个数,表示这 n 个砝码的重量。
第三行为 m 个数,表示这 m 个物品的重量。

输出格式
输出 m 行,对于第 i 行,如果第 i 个物品能被称出,输出 YES,否则输出 NO。

样例输入
4 2
1 2 4 8
15 16

样例输出
YES
NO

数据范围
1 ≤ n ≤ 24,
1 ≤ m ≤ 10.


题解
DFS:

决策

  1. 不选用当前的砝码
  2. 将当前的砝码放在左边(砝码的一边)
  3. 将当前的砝码放在右边(物品的一边)
#include 
using namespace std;

const int N = 30;

int n, m;
int w[N], s[N];
bool flag = false;

void dfs(int u, int l, int r)
{	
	if(u == n + 1)
	{
		if(l == r) flag = true;;		
		return;
	}
	
	dfs(u + 1, l, r);
	dfs(u + 1, l + s[u], r);
	dfs(u + 1, l, r + s[u]);
}

int main()
{
	cin >> n >> m;
	
	for (int i = 1; i <= n; i ++) cin >> s[i];
	for (int i = 1; i <= m; i ++) cin >> w[i];
	
	for (int i = 1; i <= m; i ++) 
	{
		flag = false;
		dfs(1, 0, w[i]);
		
		if(flag) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
		
	return 0;	
}

错解
01背包:

这样不能通过全部的数据,因为没有考虑到第三种情况。

假设有两个砝码,分别为 5g2g;有一个 3g 的物品。

按照01背包的解法,这个物品是称不出来的,但是如果将较小的砝码与物品放在一起,就可以了,即 5 = 2 + 3

#include 
#include 
using namespace std;

const int N = 10010;

int w[N], s[N], f[N];

int main()
{
	int n, m;
	cin >> n >> m;
	
	for (int i = 1; i <= n; i ++) cin >> s[i];
	for (int i = 1; i <= m; i ++) cin >> w[i];
	
	for (int k = 1; k <= m; k ++)
	{
		memset(f, 0, sizeof f);
		f[0] = 1;
		for (int i = 1; i <= n; i ++)
			for (int j = w[k]; j >= 0; j --)
				f[j] += f[j - s[i]];
		
		if(f[w[k]]) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	
	return 0; 
}

你可能感兴趣的:(蓝桥杯练习系统)