zoukankan      html  css  js  c++  java
  • 【2019HDU多校】第九场1006/HDU6685-Rikka with Coin——位运算打表

    题目链接

    题目大意

    使用10、20、50、100元面额的硬币能分别组成题目给出的面额,需要最少的硬币个数

    分析

    一开始队友想用一堆if-else解决问题,然后WA了无数发……

    我想到了一种比较简单的打表法来解决这个问题,而这个表长度只有13个int

    在开始分析之前,我们先不考虑出现 -1 的解。即出现某种情况 mod 10不等于0,因为这个判断非常简单

    定律

    开始推这个表之前先确定一个显而易见的定律

    若存在两种方案的需要的硬币数一样,且第一种的方案能组成的面额第二种都可以组成,则第一种方案不可取。

    证明:如果我使用了第一组方案,则我必定可以使用第二种方案,即使第二种方案不能组成其他更多的面额,这样的选择也是完全没有错误的

    推论

    根据定律,可以得到下面这些推论

    1、不存在一种方案包含两个10元、同理有两个50元也不存在

    证明:如果存在一个方案包含两个10元,则我们可以选择用一个10元和一个20元代替这个方案。我们可以确定,两个10元只能组成10、20这两个面额。而10元和20元可以组成10元、20元、30元三个面额。根据定律,则确定此方案不可行。同理,两个50元可以用50+100代替

    2、仅使用4个非100元的硬币,只有两种组成方案:10、20、20、50和20、20、20、50

    证明:对于4个硬币,方案10、20、20、50可以组成10-100的所有情况,所以任何其他方案如果合理,则必须能组成有超过100元的情况。根据推论1,则50元不可以重复,所以能组成的最大的值就是用20、20、20、50组成110。

    3、不存在使用5个非100元的硬币的情况

    证明:首先如果使用了5个硬币,根据推论1,可以得到的组合仅两种:10、20、20、20、50即120元,和20、20、20、20、50即130元。那么无论这个组合怎么样,通过推论2的结论,都不如10、20、20、50、100这个组合,因为这个组合能完成10-200以内的所有解。那么根据定律,上述两个组合都是错误的组合。

    4、大于110元的面额必须要通过一个或者数个100元的硬币来组成

    证明:这个很简单,通过推理3可以直接推出。所以对于大于110元的面额都应不断的-100直到满足上述情况

    打表

    那么我们知道了最多只有4个非100元的硬币,那么我们就可以得到所有的组合情况(省略0)

    1
    2
    5
    1 2
    1 5
    2 2
    2 5
    1 2 2
    1 2 5
    2 2 2
    2 2 5
    1 2 2 5
    2 2 2 5

    那么我们可以把这些情况能组成的数字打表出来

    1
    2
    5
    1 2 3
    1 5 6
    2 4
    2 5 7
    1 2 3 4 5
    1 2 3 5 6 7 8
    2 4 6
    2 4 5 7 9
    1 2 3 4 5 6 7 8 9 10
    2 4 6 7 9 11

    借助位运算,我们可以这样做:

    print(1 << 1)
    print(1 << 2)
    print(1 << 5)
    print((1 << 1) + (1 << 2) + (1 << 3))
    print((1 << 1) + (1 << 5) + (1 << 6))
    print((1 << 2) + (1 << 4))
    print((1 << 2) + (1 << 5) + (1 << 7))
    print((1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5))
    print((1 << 1) + (1 << 2) + (1 << 3) + (1 << 5) + (1 << 6) + (1 << 7) + (1 << 8))
    print((1 << 2) + (1 << 4) + (1 << 6))
    print((1 << 2) + (1 << 4) + (1 << 5) + (1 << 7) + (1 << 9))
    print((1 << 1) + (1 << 2) + (1 << 3) + (1 << 4) + (1 << 5) + (1 << 6) + (1 << 7) + (1 << 8) + (1 << 9) + (1 << 10))
    print((1 << 2) + (1 << 4) + (1 << 5) + (1 << 6) + (1 << 7) + (1 << 9) + (1 << 11))
    

    可以尝试理解这个表的原理,即将每一个bite作为表的一个元素

    可以看到最大值只有110元。而注意到面额最大的硬币为100。而110元可以用非100元组成也可以通过10元+100元组成,同理100元也有两种组合方式。所以需要考虑四个情况:
    1、100元用100元组成、110元用100元组成
    2、100元用100元组成、110元用非100元组成
    3、100元用非100元组成、110元用100元组成
    4、100元用非100元组成、110元用非100元组成

    那么都考虑一下,取较小者

    AC-Code

    #include<bits/stdc++.h>
    
    using namespace std;
    
    vector<int> res;
    
    void init() {
        res.push_back(0);
        res.push_back(2);
        res.push_back(4);
        res.push_back(32);
        res.push_back(14);
        res.push_back(98);
        res.push_back(20);
        res.push_back(164);
        res.push_back(62);
        res.push_back(494);
        res.push_back(84);
        res.push_back(692);
        res.push_back(2046);
        res.push_back(2804);
    }
    
    int get_ans(int as) {
        int t = -1;
        for (int i = 0; i < res.size(); ++i) {
            if ((res[i] ^ as) + (res[i] & as) == res[i]) {
                t = i;
                break;
            }
        }
        switch (t) {
            case 0:
                return 0;
            case 1:
            case 2:
            case 3:
                return 1;
            case 4:
            case 5:
            case 6:
            case 7:
                return 2;
            case 8:
            case 9:
            case 10:
            case 11:
                return 3;
            case 12:
            case 13:
                return 4;
        }
        return 1000;
    }
    
    int main() {
    #ifdef ACM_LOCAL
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
        auto start_clock_for_debug = clock();
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin.tie(0);
    
        int t;
        cin >> t;
        init();
        while (t--) {
            int n;
            cin >> n;
    
            int hun[4];
            int cnt[4][15];
            memset(cnt, 0, sizeof(cnt));
            memset(hun, 0, sizeof(hun));
            bool flag = false;
    
            for (int i = 0; i < n; ++i) {
                int tmp;
                cin >> tmp;
    
                if (flag) continue;
    
                if (tmp % 10) {
                    flag = true;
                    continue;
                }
                // 100 + 110
                int tmp0 = tmp;
                if (tmp0 % 100 > 10 and tmp0 > 100) {
                    hun[0] = max(tmp0 / 100, hun[0]);
                    tmp0 %= 100;
                    cnt[0][tmp0 / 10]++;
                } else if (tmp0 > 100) {
                    hun[0] = max((tmp0 - 100) / 100, hun[0]);
                    tmp0 %= 100;
                    cnt[0][tmp0 / 10 + 10]++;
                } else {
                    cnt[0][tmp0 / 10]++;
                }
                // 100
                int tmp1 = tmp;
                if (tmp1 % 100 == 0 and tmp1 > 100) {
                    hun[1] = max((tmp1 - 100) / 100, hun[1]);
                    cnt[1][10]++;
                } else {
                    hun[1] = max(tmp1 / 100, hun[1]);
                    cnt[1][(tmp1 % 100) / 10]++;
                }
                // 110
                int tmp2 = tmp;
                if (tmp2 % 100 == 10 and tmp2 > 100) {
                    hun[2] = max((tmp2 - 100) / 100, hun[2]);
                    cnt[2][11]++;
                } else {
                    hun[2] = max(tmp2 / 100, hun[2]);
                    cnt[2][(tmp2 % 100) / 10]++;
                }
                // None
                int tmp3 = tmp;
                hun[3] = max(tmp3 / 100, hun[3]);
                cnt[3][(tmp3 % 100) / 10]++;
            }
            if (flag) {
                cout << -1 << endl;
                continue;
            }
            int ans;
            int get_ans[4];
    
            for (int j = 0; j < 4; ++j) {
                ans = 0;
                for (int k = 1; k < 13; ++k) {
                    if (cnt[j][k]) {
                        ans += 1 << k;
                    }
                }
                get_ans[j] = get_ans(ans) + hun[j];
            }
            sort(get_ans, get_ans + 4);
            cout << get_ans[0] << endl;
        }
    #ifdef ACM_LOCAL
        auto end_clock_for_debug = clock();
        cerr << "Run Time: " << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
    #endif
        return 0;
    }
    
  • 相关阅读:
    Linux命令发送Http的get或post请求(curl和wget两种方法)
    大数据面试题以及答案整理(一)
    大数据面试题及答案-汇总版
    Linux shell之打印输出
    Java开发中常见的危险信号(上)
    sencha touch笔记(5)——DataView组件(1)
    sencha touch(7)——list组件
    sencha touch笔记(6)——路由控制(1)
    [置顶] Android源码分析-点击事件派发机制
    UVa 10330 Power Transmission / 最大流
  • 原文地址:https://www.cnblogs.com/mauve-hkq/p/12173144.html
Copyright © 2011-2022 走看看