zoukankan      html  css  js  c++  java
  • AOJ-743-多重部分和问题

    Description
    有n种不同大小的数字,每种各个。判断是否可以从这些数字之中选出若干使它们的和恰好为K。

    Input
    首先是一个正整数T(1<=T<=100)
    接下来是T组数据
    每组数据第一行是一个正整数n(1<=n<=100),表示有n种不同大小的数字
    第二行是n个不同大小的正整数ai(1<=ai<=100000)
    第三行是n个正整数mi(1<=mi<=100000),表示每种数字有mi个
    第四行是一个正整数K(1<=K<=100000)

    Output
    对于每组数据,如果能从这些数字中选出若干使它们的和恰好为K,则输出“Yes”,否则输出“No”,每个输出单独占一行

    Sample Input
    2
    3
    3 5 8
    3 2 2
    17
    2
    1 2
    1 1
    4

    Sample Output
    Yes
    No

    Source
    安徽省2015年“京胜杯”大学生程序设计竞赛

    —————————并不华丽的分界线————————————
    很明显,这是一个入门的多重背包问题。
    对于背包问题,想要了解的更多的可以去看看背包九讲,或者是2009年国家集训队论文《浅谈几类背包问题》(作者是浙江省温州中学徐持衡)
    对于这个背包问题,很明显只需要使用普通的解法就可以了,也就是把每种物品的数量分成1,2,4,8…..V[i] - 2^n -1(V[i]表示第i个物品的数量,其价值为A[i])就可以了。
    在做多重背包时,第一个想法必然是把多重背包转化为01背包,但是这个时候,它的时间复杂度就会比较高,可能能达到O(NM∑Vi),那么这个算法就不是很可取了,但是如果考虑把V[i]化成V[i] = 1 + 2 + 4 + …. + V[i]-2^n-1且每个物品的价值也以此来改变,那么其时间复杂度可以大大优化。当然,如果想要获得更大的优化,可以使用单调队列优化。
    所以这道题就非常容易了,代码如下:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 100010;
    int a[maxn];
    int m[maxn];
    int f[maxn];
    
    bool judge(int n, int k){
        for(int i = 0; i <= k; i++){
            f[i] = 0;
        }
        for(int i = 0; i < n; i++){
            int t = 1;
            while(m[i] > 0){
                if(t > m[i]){
                    t = m[i];
                }
                m[i] = m[i] - t;
    
                for(int j = k; j >= a[i] * t; j--){
                    f[j] = max(f[j], f[j - t * a[i]] + t * a[i]);
                }
                t *= 2;
            }
        }
        for(int j = 0; j <= k; j++){
            if(f[j] == k)
                return true;
        }
        return false;
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--){
            int n, k;
            scanf("%d",&n);
            for(int i = 0; i < n; i++){
                scanf("%d",&a[i]);
            }
            for(int i = 0; i < n; i++){
                scanf("%d",&m[i]);
            }
            scanf("%d",&k);
            if(judge(n, k)){
                printf("Yes
    ");
            }else{
                printf("No
    ");
            }
        }
        return 0;
    }

    本人渣渣,如有哪里错误,请各位大牛指正。

  • 相关阅读:
    链表中环的入口节点
    链表中倒数第k个节点
    调整数组顺序使奇数位于偶数前面
    53. Maximum Subarray
    42. Trapping Rain Water
    48. Rotate Image
    css技巧一
    html语义
    label标签表单响应
    CSS清除浮动
  • 原文地址:https://www.cnblogs.com/wiklvrain/p/8179499.html
Copyright © 2011-2022 走看看