2144 砝码称重 2

2144 砝码称重 2

http://codevs.cn/problem/2144/

题目描述 Description

有n个砝码,现在要称一个质量为m的物体,请问最少需要挑出几个砝码来称?

注意一个砝码最多只能挑一次

输入描述 Input Description

第一行两个整数n和m,接下来n行每行一个整数表示每个砝码的重量。

输出描述 Output Description

输出选择的砝码的总数k,你的程序必须使得k尽量的小。

样例输入 Sample Input

3 10
5
9
1

样例输出 Sample Output

2

数据范围及提示 Data Size & Hint

1<=n<=30,1<=m<=2^31,1<=每个砝码的质量<=2^30

方法一:深搜,用后缀和优化

读入数据用读入优化

剪枝1:如果当前使用的砝码数>=当前最优解,return

剪枝2:深搜之前按从大到小排序,如果当前重量+当前砝码的后缀和<m ,return

剪枝3:如果当前重量+当前砝码重量>m ,换下一个砝码,注意不能return

因为砝码从大到小排序,后面的后缀和一定小于前面的,所以如果当前重量+当前砝码的后缀和<m,那么后面的更小,所以直接return

而如果当前重量+当前砝码重量>m ,下一个砝码的质量更小,所以有可能产生答案,所以不能return

总耗时:201ms

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,ans;
long long a[31],suf[31];
void dfs(int now,int use,long long wei)//now当前第几个砝码,引入这个变量避免了很多重复搜索;use当前使用砝码总数;wei当前使用砝码总重量
{
    if(use>=ans) return;//当前砝码使用量>=当前最优解
    if(wei==m) ans=min(ans,use);
    for(int i=now+1;i<=n;i++)
    {
        if(wei+suf[i]<m) return;//当前重量+当前砝码后缀和<目标质量
        if(wei+a[i]>m) continue;//当前重量+当前砝码重量>目标重量
        dfs(i,use+1,wei+a[i]);
    }
}
long long init()//读入优化
{
    long long x=0;char c=getchar();
    while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘) {x=x*10+c-‘0‘;c=getchar();}
    return x;
}
bool cmp(long long p,long long q) {return p>q;}
int main()
{
     n=init();m=init();
     for(int i=1;i<=n;i++)  a[i]=init();
     sort(a+1,a+n+1,cmp);//从大到小排序
     for(int i=n;i;i--) suf[i]=suf[i+1]+a[i];//后缀和
     ans=n;
     dfs(0,0,0);
     printf("%d",ans);
}

方法二:双向搜索

每次只深搜前一半,深搜完了在深搜后一半,如果在搜索后一般的过程中,发现结果有与前一半的搜索结果相加等于m的,那就用这两部分的步数和相加更新答案

不用hash 51ms

#include<map>
#include<iostream>
#include<algorithm>
using namespace std;

int n, mass, ans(666), f[233];
map<int, int>m;//能称出的质量→需要的砝码

void dfs(int step, int last, int sum, bool k) {
    int r(n);//右边界
    if (k)//如果是前半段
        m[sum] = step, r /= 2;//记录搜到的所有解
    else//后半段
    if (m.find(mass - sum) != m.end())//如果能跟前半段的结果组成目标质量
        ans = min(ans, step + m[mass - sum]);//更新答案
    for (int i(last); i < r; ++i) //生成全组合
        dfs(step + 1, i + 1, sum + f[i], k);
}

int main() {
    cin >> n >> mass;
    for (int i(0); i < n; ++i)
        cin >> f[i];
    dfs(0, 0, 0, true);//先搜前半段
    dfs(0, n / 2, 0, false);//再搜后半段
    cout << ans << endl;
    return 0;
}

方法三:方法二的原理+hash

用hash  46ms,应该是数据比较水,不然hash应该更快

#include<cstdio>
#include<iostream>
#include<algorithm>
#define mod1 1009
#define mod2 10000007
using namespace std;
int n,m,ans,cnt;
long long a[31];
int head[1009];
struct node
{
    int next,w,to;//w表示hash值为to时需要的步骤数
}e[5000001];
int get_hash1(long long x)//双模哈希
{
    return x%mod1;
}
int get_hash2(long long x)
{
    return x%mod2;
}
long long init()//读入优化
{
    long long x=0;char c=getchar();
    while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘) {x=x*10+c-‘0‘;c=getchar();}
    return x;
}
void add(int u,int v,int t)
{
    cnt++;
    e[cnt].to=v;
    e[cnt].w=t;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int find(int s,int z)
{
    for(int i=head[s];i;i=e[i].next)
        if(e[i].to==z) return e[i].w;
    return -1;
}
void dfs(int now,int use,long long wei,bool judge)
//now当前第几个砝码,引入这个变量避免了很多重复搜索;use当前使用砝码总数;wei当前使用砝码总重量;judge=0表示搜索前一半,=1搜索后一半
{
    int r=n;//右边界
    if(!judge)
    {
        add(get_hash1(wei),get_hash2(wei),use);
        r/=2;//只搜前一半
    }
    else
    {
        int h1=get_hash1(m-wei),h2=get_hash2(m-wei);//hash
        int p=find(h1,h2);
        if(p>=0) ans=min(ans,p+use);
    }
    for(int i=now+1;i<=r;i++)
      if(wei+a[i]<=m)
        dfs(i,use+1,wei+a[i],judge);
}
int main()
{
     n=init();m=init();
     ans=n;
     for(int i=1;i<=n;i++)  a[i]=init();
     dfs(0,0,0,0);//搜索前一半
     dfs(n/2,0,0,1);//搜索后一半
     printf("%d",ans);
}
时间: 2024-10-23 05:57:07

2144 砝码称重 2的相关文章

wiki 2144 砝码称重 2 STL_map

前言       最近学习C#,不过好在当初考计算机二级学习过C++,刚上手没有对C#感到很恐惧.C#视频也看了几天 了,总感觉不总结一下心里没底,现在跟着我从头走进C#之旅吧.     C#是以后总面向对象的编程语言(OOP),C#是从C和C++派生出来的,主要用于开发可以运行在.NET平台 上的应用程序.随着.NET的发展,C#语言简单.现代.面向对象和类型安全显示了一定的优势.     下面我就介绍一些初学者不太理解的一些东西.   C#有以下突出的特点       (1)语法简洁.不允许

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

HFUT 1354.砝码称重(安徽省2016“京胜杯”程序设计大赛 A)

砝码称重 Time Limit: 1000 MS Memory Limit: 65536 KB Total Submissions: 12 Accepted: 10 Description 小明非常喜爱物理,有一天,他对物理实验室中常用的弹簧拉力计产生了兴趣.实验室中有两种质量不同的砝码,小明分别用a个第一种砝码放在弹簧拉力计上和b个第二种砝码放在弹簧拉力计上,假设每增加单位重量的砝码,弹簧拉力计的长度增加1,那么两次称量弹簧拉力计的长度差是多少呢?(假设拉力计不发生非弹性形变) Input 第

砝码称重问题二

题目描述 有一组砝码,重量互不相等,分别为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) 解题 参考智力题砝码称重

蓝桥杯——说好的进阶之砝码称重(贪心算法)

5个砝码 用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量. 如果只有5个砝码,重量分别是1,3,9,27,81.则它们可以组合称出1到121之间任意整数重量(砝码允许放在左右两个盘中). 本题目要求编程实现:对用户给定的重量,给出砝码组合方案. 例如: 用户输入: 5 程序输出: 9-3-1 用户输入: 19 程序输出: 27-9+1 要求程序输出的组合总是大数在前小数在后. 可以假设用户的输入的数字符合范围1~121. public static void main(String

51nod 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.

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 第

C程序设计的抽象思维-递归过程-砝码称重

[问题] 在狄更斯时代,商人们用砝码和天平来称量商品的重量,如果你只有几个砝码,就只能精确地称出一定的重量.例如,假定只有两个砝码:分别是1kg和3kg.只用1kg的砝码可以称出1kg重量的商品,只用3kg的砝码可以称出3kg重量的商品.1kg和3kg的砝码放在天平同一边可以称出4kg重量的商品,放在不同边可以称出2kg重量的商品.因此利用这两个砝码,我们可以称出重量分别为1.2.3.4kg的商品. 编写一个递归函数: bool IsMeasurable(int target, int weig