题目描述
有一天,他在宿舍里无意中发现了一个天平!这 个天平很奇怪,有n个完好的砝码,但是没有游码。盾神为他的发现兴奋不已!于是他准备去称一称自己的东西。他准备好了m种物品去称。神奇的是,盾神一早就 知道这m种物品的重量,他现在是想看看这个天平能不能称出这些物品出来。但是盾神稍微想了1秒钟以后就觉得这个问题太无聊了,于是就丢给了你。
输入
第一行为两个数,n和m。
第二行为n个数,表示这n个砝码的重量。第三行为m个数,表示这m个物品的重量。
数据规模和约定
1< =n< =24, 1< =m< =10.输出
输出m行,对于第i行,如果第i个物品能被称出,输出YES否则输出NO。
样例输入
4 2 1 2 4 8 15 16样例输出
YES NO
注:题目中没有说明,天平两侧砝码都可以放
思路一:对于砝码有放在左侧,放在右侧或不放三种选择。而且,此题数据量不大,可以dfs求解出所有的可能性。
代码如下:
#include <iostream>//砝码可以放在天平的任意一侧 #include <cstdio>//dfs #include <cstring> #include <algorithm> #include <cmath> #include <map> #include <set> #include <stack> #include <queue> #include <vector> #include <string> #define cla(a, sum) memset(a, sum, sizeof(a)) #define rap(i, m, n) for(int i=m; i<=n; i++) #define rep(i, m, n) for(int i=m; i>=n; i--) #define bug printf("???\n") using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> P; typedef pair<ll, ll> Pl; const int Inf = 0x3f3f3f3f; const double eps = 1e-8; const int mod=998244353; const int maxn = 1e6+5; const int N=1e5; template <typename T> void read(T &x){ x = 0; int f = 1; char ch = getchar(); while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();} while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();} x *= f; } //dp为标记数组,记录所有可能性 int n,m,sum=0; int dp[maxn]={0},a[55],b[55]; int ok[55]; void dfs(int x,int y,int pos){ if(pos>n+1)return ; int c=abs(x-y); dp[c]=1; dfs(x,y,pos+1); dfs(x+a[pos],y,pos+1); dfs(x,y+a[pos],pos+1); } int main() { cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; sum+=a[i]; } for(int i=1;i<=m;i++) cin>>b[i]; dfs(0,0,1); for(int i=1;i<=m;i++){ if(b[i]>sum||dp[b[i]]==0)printf("NO\n"); else printf("YES\n"); } return 0; }
思路二:一般来说,如果数据量较大,dfs会超时。此类题目也可以转化为动态规划问题。
用dp[i][j]来表示前i个砝码,是否可以称出j,然后要不加ai,要不减ai,要不不加也不减 。(1表示可以,0表示不可以)
代码如下
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <map> #include <set> #include <stack> #include <queue> #include <vector> #include <string> #define cla(a, sum) memset(a, sum, sizeof(a)) #define rap(i, m, n) for(int i=m; i<=n; i++) #define rep(i, m, n) for(int i=m; i>=n; i--) #define bug printf("???\n") using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> P; typedef pair<ll, ll> Pl; const int Inf = 0x3f3f3f3f; const double eps = 1e-8; const int mod=998244353; const int maxn = 1e6+5; const int N=1e5; template <typename T> void read(T &x){ x = 0; int f = 1; char ch = getchar(); while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();} while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();} x *= f; } int n,m,a[55]; int V=0,dp[35][500005]; int ok[500005],c; //用dp[i][j]来表示前i个砝码,是否可以称出j,然后要不加ai //要不减ai,要不不加也不减 int main() { cin>>n>>m; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); V+=a[i]; } dp[0][V]=1; //防止出现负数,扩大V, -V~V 变为 0~2*V //dp[0][V]相当于dp[0][0] for(int i=1;i<=n;i++){ for(int j=0;j<=V*2;j++){ if(dp[i-1][j]){ dp[i][j]=1; if(j+a[i]<=V*2) dp[i][j+a[i]]=1; if(j-a[i]>=0) dp[i][j-a[i]]=1; } } } for(int i=V+1;i<=2*V;i++) if(dp[n][i]) ok[i-V]=1; for(int i=1;i<=m;i++){ scanf("%d",&c); if(c>V) printf("NO\n"); else if(ok[c]) printf("YES\n"); else printf("NO\n"); } return 0; } /* 2 2 1 3 2 4 */