dp + 容斥 硬币购物

题目链接: https://www.luogu.com.cn/problem/P1450
题意:
共有 4 种硬币。面值分别为 c1,c2,c3,c4

某人去商店买东西,去了 n 次,对于每次购买,他带了 di 枚 i 种硬币,想购买 s 的价值的东西。请问每次有多少种付款方法。

输入格式

输入的第一行是五个整数,分别代表 c1,c2,c3,c4,n 范围: M = 1e5
接下来 n (n <= 1000) 行,每行有五个整数,描述一次购买,分别代表 d1,d2,d3,d4,s

解题思路:
首先, 题意很容易让人想到多重背包模型, 但是由于有 n 次询问, 所以时间复杂度不可取
如果硬币的数量没有限制就好了, 假如没有限制, 那么最后算出来的方案数就会多, 那其实减去多去的方案数就行了, 多的方案数的情况为: 一种硬币数量超过限制, 两种…四种, 他们之间两两组合, 最多有 16 种组合情况, 并且发现两种硬币数量超过限制的情况是包含一种情况的, 所以这里很容易想到容斥原理来算出不符合限制的情况的
所以最后的方法就是: 预处理完全背包的方案数 (注意和完全背包模板不同), 然后运用容斥原理来减去硬币超过限制方案数 复杂度O(M)

代码:

public class Main {
    
    
    private static final int N = (int)(1e5 + 5);
    private static long f[] = new long[N];
    private static long c[] = new long[10];
    private static long d[] = new long[10];
    private static int s;
    private static long ans = 0;

    public static void main(String[] args) {
    
    
        InputReader in = new InputReader();
        PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
        for(int i = 1; i <= 4; i++) {
    
    
             c[i] = Integer.parseInt(in.next());
        }
        int n = Integer.parseInt(in.next());
        f[0] = 1;
        // 完全背包方案数求法 和 完全背包模板有不同
        for(int i = 1; i <= 4; i++) {
    
    
            for(int j = (int)c[i]; j < N; j++) {
    
    
                f[j] += f[j - (int)c[i]];
            }
        }
        while(n-- > 0) {
    
    
            for(int i = 1; i <= 4; i++) {
    
    
                d[i] = Integer.parseInt(in.next());
            }
            s = Integer.parseInt(in.next());
            ans = f[s];
            // 容斥原理 二进制枚举组合
            for(int i = 1; i <= 15; i++) {
    
    
                int now = s, k;
                for(int tmp = i, j = k = 1; tmp > 0; tmp >>= 1, j++) {
    
    
                    if((tmp & 1) == 1) {
    
    
                    // 通过 k 来判定是加还是减
                        k ^= 1;
                        now -= (d[j] + 1) * c[j];
                    }
                }
                if(now >= 0) {
    
    
                    if(k > 0) {
    
    
                        ans += f[now];
                    } else {
    
    
                        ans -= f[now];
                    }
                }
            }
            out.println(ans);
            out.flush();
        }


    }

    static class InputReader {
    
    
        BufferedReader br = null;
        StringTokenizer tok = null;
        InputReader() {
    
    
            br = new BufferedReader(new InputStreamReader(System.in));

        }
        boolean hasNext() {
    
    
            while(tok == null || !tok.hasMoreElements()) {
    
    
                try{
    
    
                    tok = new StringTokenizer(br.readLine());
                } catch (Exception e) {
    
    
                    return false;
                }
            }
            return true;
        }
        String next() {
    
    
            return hasNext() ? tok.nextToken() : null;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_45560445/article/details/121385236