• BZOJ1042 [HAOI2008]硬币购物


    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

    Description

      硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
    i的价值的东西。请问每次有多少种付款方法。

    Input

      第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000

    Output

      每次的方法数

    Sample Input

    1 2 5 10 2
    3 2 3 1 10
    1000 2 2 2 900

    Sample Output

    4
    27

     

     

    正解:DP+容斥原理

    解题报告:

      这题显然爆搜没救QAQ

      考虑我们如果直接$DP$的话无法保证不超过限制,那么对于这种有限制的问题,我们可以很快想到用容斥原理。
      先预处理出不含限制的情况下的方案数,再想想如何处理限制。

      根据容斥的惯用思路,都是对于若干限制,满足其中一些,然后根据奇偶性进行判断。

      所以我们只要用全集$-$打破$1$个限制的方案$+$打破$2$个的$-$打破$3$个的$+$打破$4$个的,即可得到满足要求的答案。

      考虑如何求出打破特定个限制的方案数,我们先枚举打破哪些限制,再强制其买$d[i]+1$个,就可以保证已经打破,剩下的就是无限制的情况了。

      只需对于每次询问都$2^4$处理一次就可以了。  

     

    //It is made by ljh2000
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    using namespace std;
    typedef long long LL;
    const int MAXN = 100011;
    int c[12],m,d[12],n;
    LL f[MAXN],ans;
    
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void dfs(int x,int remain,int num){
    	if(x==5) {
    		if(num&1) ans-=f[remain];
    		else ans+=f[remain];
    		return ;
    	}
    	int now=c[x]*(d[x]+1);
    	if(remain>=now) dfs(x+1,remain-now,num+1);
    	dfs(x+1,remain,num);
    }
    
    inline void work(){
    	for(int i=1;i<=4;i++) c[i]=getint(); 
    	m=getint(); f[0]=1;
    	//为避免方案重复计算,必须按不同种类的硬币划分阶段!
    	for(int j=1;j<=4;j++) {
    		for(int i=1;i<=100000;i++) {		
    			if(i<c[j]) continue;
    			f[i]+=f[i-c[j]];
    		}
    	}
    	while(m--) {
    		for(int i=1;i<=4;i++) d[i]=getint(); n=getint();
    		ans=0;
    		dfs(1,n,0);
    		printf("%lld
    ",ans);
    	}
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    本月时间按天显示
    微信小程序----当前时间的时段选择器插件(今天、本周、本月、本季度、本年、自定义时段)
    vuex进行传值
    echart 自定义 formatter
    git 登录流程
    Java学习-反射
    mysql数据类型char、varchar、text的一些区别
    微信小程序踩坑记录
    Rancher、Helm、HelmFile
    句子迷 2015_01_10
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6346433.html
Copyright © 2020-2023  润新知