zoukankan      html  css  js  c++  java
  • 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);
    }
  • 相关阅读:
    html总结:背景图片拉伸
    html总结:表格中的文字居中
    html总结:float实现span和input输入框同行
    servlet总结:Servlet基础
    js总结:JavaScript入门
    河北科技创新平台年报系统涉众分析
    确定业务问题的范围-上下文的范围模型
    问题账户需求分析
    Struts2------OGNL表达式
    Struts入门
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6227504.html
Copyright © 2011-2022 走看看