J - 砝码称重 改自51nod1449

J - 砝码称重

Time Limit: 2000/1000 MS (Java/Others)      Memory Limit: 128000/64000 KB (Java/Others)

Submit Status

Problem Description

有一个物品和一些已知质量的砝码和天平,问能不能用这些砝码称出这个物品的重量(天平两边都可以放砝码)

Input

多样例输入,样例数<=20000

对于每个样例:

第一行输入两个数n,m,表示砝码数量和重物质量,1 ≤ m ≤ 1018

第二行输入n个数a1 ,a2 , ... ,an ,1018 ≥ ai+1 ≥ 3 * ai ≥ 1

Output

Case x: y

x代表第几个样例,若能称出重物的质量,y为YES,否则y为NO

Sample Input

3 7
1 3 9

Sample Output

Case 1: YES

Hint

样例为一边盘放重物和质量为3的砝码,另一边盘放质量为1和9的砝码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int N=1e5+10;
 5 const int INF=0x3f3f3f3f;
 6 int cas=1,T;
 7 int n,vis[40];
 8 LL a[40],b[40],m;
 9 bool dfs(int x,LL sum,int v)
10 {
11     if(sum==0) return 1;
12     if(x<0 || b[x]<sum || sum<0) return 0;
13     if(!vis[x])
14     {
15         vis[x]=v;
16         if(dfs(x-1,sum-a[x],v)) return 1;
17         vis[x]=0;
18     }
19     if(dfs(x-1,sum,v)) return 1;
20     return 0;
21 }
22 bool solve(LL sum,int v)
23 {
24     if(sum<0) return 0;
25     int id=0;
26     while(a[id]<=sum) id++;
27     if(dfs(id-1,sum,v)) return 1;
28     if(!vis[id-1])
29     {
30         vis[id-1]=v;
31         if(solve(sum-a[id-1],v)) return 1;
32         vis[id-1]=0;
33     }
34     if(!vis[id])
35     {
36         vis[id]=v;
37         if(solve(a[id]-sum,v^1)) return 1;
38         vis[id]=0;
39     }
40     return 0;
41 }
42 int main()
43 {
44 //    freopen("1.in","w",stdout);
45     freopen("1.in","r",stdin);
46     freopen("1.out","w",stdout);
47 //    scanf("%d",&T);
48     while(scanf("%d%lld",&n,&m)==2)
49     {
50         memset(vis,0,sizeof(vis));
51         a[0]=b[0]=0;
52         for(int i=1;i<=n;i++)
53         {
54             scanf("%lld",a+i);
55             b[i]=b[i-1]+a[i];
56         }
57         a[n+1]=1LL*INF*INF;
58         b[n+1]=b[n]+a[n+1];
59         vis[0]=vis[n+1]=1;
60         printf("Case %d: ",cas++);
61         if(solve(m,2))
62         {
63             int sum1=0,sum2=0;
64             puts("YES");
65             /*for(int i=1;i<=n;i++) if(vis[i]==2) printf("%d ",a[i]),sum1+=a[i];
66             printf("\n");
67             for(int i=1;i<=n;i++) if(vis[i]==3) printf("%d ",a[i]),sum2+=a[i];
68             printf("\n");
69             printf("sum1:%d sum2:%d m:%d dif:%d\n",sum1,sum2,w,sum1-sum2);*/
70         }
71         else puts("NO");
72     }
73 //    printf("%d %d\n",clock(),CLOCKS_PER_SEC);
74 //    printf("time=%.3f\n",(double)clock()/CLOCKS_PER_SEC);
75     return 0;
76 }

solve.cpp

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef unsigned long long ULL;
 4 typedef long long LL;
 5 const int INF = 0x3f3f3f3f;
 6 const double eps = 1e-9;
 7 long long a[40];
 8 int vis[40];
 9 int cas=1;
10 int check(int n, LL x, LL p)
11 {
12     for(int i=n-1;i>=0;--i)
13     {
14         if(x - a[i] >= p) x -= a[i], vis[i] = 0;
15         if(x >= p + a[i]) p += a[i], vis[i] = 2;
16     }
17     if(x == p) return 1;
18     else return 0;
19 }
20
21 int main()
22 {
23 //    freopen("1.in","r",stdin);
24 //    freopen("t1.out","w",stdout);
25     int T, cas=1;
26     int n;
27     LL m;
28     while(scanf("%d%lld", &n, &m) == 2)
29     {
30         for(int i=0;i<n;++i)
31             scanf("%lld", &a[i]);
32         sort(a, a+n);
33         int flag = 0;
34         long long sum = m;
35         for(int i=0;i<n&&!flag;++i)
36         {
37             memset(vis, 0, sizeof vis);
38             for(int j=0;j<i;++j) vis[j] = 1;
39             vis[i] = 2;
40             if(sum == a[i])
41                 flag = 1;
42             else
43             {
44                 flag = check(i, sum, a[i]);
45                 if(!flag)
46                 {
47                     memset(vis, 0, sizeof vis);
48                     for(int j=0;j<i;++j) vis[j] = 1;
49                     flag = check(i, sum, 0);
50                 }
51                 sum += a[i];
52             }
53         }
54         printf("Case %d: ",cas++);
55         puts(flag?"YES":"NO");
56 /*        if(flag)
57         {
58             LL sum = m;
59             for(int i=0;i<n;++i) if(vis[i] == 1) printf("%lld ", a[i]), sum += a[i]; puts("");
60             printf("lsum:%lld\n", sum);
61             sum = 0;
62             for(int i=0;i<n;++i) if(vis[i] == 2) printf("%lld ", a[i]), sum += a[i]; puts("");
63             printf("rsum:%lld\n", sum);
64         }*/
65     }
66
67 //    printf("time=%.3lf\n",(double)clock()/CLOCKS_PER_SEC);
68     return 0;
69 }

solve.cpp

题解:

由砝码质量的上限可知n只有几十。
若a[i]刚好大于m,则不会用到a[i+1],
因为a[1]+a[2]+...+a[i]+m < a[i+1]/2+m <= a[i+1]/2+a[i+1]/3 < a[i+1]
递归执行以下过程
1.每次搜索比m小的a[i]能否凑成m
2.若不可以凑成,则m=a[i]-m,回到1
3.若2还是不可以凑成,则m=m-a[i-1],回到1

搜索能否凑成a[i]时记得剪枝

时间: 2024-08-25 16:02:20

J - 砝码称重 改自51nod1449的相关文章

1449 砝码称重

1449 砝码称重 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 现在有好多种砝码,他们的重量是 w0,w1,w2,...  每种各一个.问用这些砝码能不能表示一个重量为m的东西. 样例解释:可以将重物和3放到一个托盘中,9和1放到另外一个托盘中. Input 单组测试数据. 第一行有两个整数w,m (2 ≤ w ≤ 10^9, 1 ≤ m ≤ 10^9). Output 如果能,输出YES,否则输出NO. Input示例

【dp】砝码称重

砝码称重 来源:NOIP1996(提高组)  第四题 [问题描述]     设有1g.2g.3g.5g.10g.20g的砝码各若干枚(其总重<=1000),用他们能称出的重量的种类数. [输入文件]   a1  a2  a3  a4  a5  a6     (表示1g砝码有a1个,2g砝码有a2个,…,20g砝码有a6个,中间有空格). [输出文件]   Total=N     (N表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况). [输入样例]     1 1 0 0 0

砝码称重问题二

题目描述 有一组砝码,重量互不相等,分别为m1.m2.m3……mn:它们可取的最大数量分别为x1.x2.x3……xn. 现要用这些砝码去称物体的重量,问能称出多少种不同的重量. 现在给你两个正整数列表w和n, 列表w中的第i个元素w[i]表示第i个砝码的重量,列表n的第i个元素n[i]表示砝码i的最大数量.  i从0开始                   请你输出不同重量的种数.如:w=[1,2], n=[2,1], 则输出5(分析:共有五种重量:0,1,2,3,4) 解题 参考智力题砝码称重

砝码称重问题

砝码称重问题:设有1g.2g.3g.5g.10g.20g的砝码各若干枚(其质量<=1000g),求出用他们能称出的质量的种类数(不包括质量为0的情况). 一.动态规划方法求解 设dp[1000]数组为标记数组.当dp[i]=0时,表示质量为i的情况,目前没有称出:当dp[i]=1时,表示质量为i的情况已经称出. 本题目中有多个砝码,我们顺序处理每一个砝码. 当处理第j个砝码,质量为wj时,有下列推导公式: 完整程序代码如下: #include<stdio.h> #include<s

洛谷P2347 砝码称重 [2017年4月计划 动态规划01]

P2347 砝码称重 题目描述 设有1g.2g.3g.5g.10g.20g的砝码各若干枚(其总重<=1000), 输入输出格式 输入格式: 输入方式:a1 a2 a3 a4 a5 a6 (表示1g砝码有a1个,2g砝码有a2个,…,20g砝码有a6个) 输出格式: 输出方式:Total=N (N表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况) 输入输出样例 输入样例#1: 1 1 0 0 0 0 输出样例#1: Total=3 暴力算法略,这里只讲dp.背包问题,每个砝码选或

P2347 砝码称重 &amp; P1474 货币系统 Money Systems

背包方案数模板题练习 第一道题是另一道也叫做"砝码称重"的前置技能,第二道题是我搜背包方案数的时候出来的. 两道题有一点区别,就是多重(01)背包和完全背包. 第一道题因为数据水,所以多重背包也能过.但是也要学会如何写多重背包!!! 第二道题是完全背包,每一种货币可以拿无穷多次. 这种背包可以理解为价值为0,只有重量. 直接给代码: 第一份的: #include<cstdio> const int b[] = {0, 1, 2, 3, 5, 10, 20}; int a[7

【练习】砝码称重

P1441 砝码称重 思路:dfs枚举去掉哪些砝码, 01背包求方案数, 各种情况取max记为ans输出√ 边界情况处理不好交了三遍QAQ dp[j] = dp[j] + dp[j - a[i]] 选上这个砝码的情况+ 不选的情况 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int sz = 2020; 6 int n, m, ans

P2347 砝码称重-DP方案数-bitset

P2347 砝码称重 DP做法 : 转化为 01背包. 进行方案数 更新.最后统计种类. #include<bits/stdc++.h> using namespace std; #define maxn 1234 int n,k,dp[maxn],len,sum,ans; int a[11]= {0,1,2,3,5,10,20}; vector<int>p; int main() { for(int i=1; i<=6; i++) { scanf("%d"

砝码称重3

参考 https://blog.csdn.net/livelylittlefish/article/details/6555347 假设有280g盐,有一架天平,有两个砝码,分别是4g和14g .能否在3次内将280g食盐分为100g和180g两堆,请详细描述你的解决方法. 这是另外一种砝码称重问题,类似,但是又不同.砝码称重这个是给定无限数量砝码,只需计算能不能组成一个重量,不用知道如何组合.砝码称重1是给定砝码个数和重量,以及可以组合的重量,求出任意一种重量是如何组合的.砝码称重2给定重量和