• [BZOJ1042]AOI2008]硬币购物


    题目描述 Description###

    硬币购物一共有4种硬币。面值分别为(c_1) ,(c_2) ,(c_3) ,(c_4) 。某人去商店买东西,去了(tot) 次。每次带(d_i)(c_i) 硬币,买(s_i) 的价值的东西。请问每次有多少种付款方法。

    输入描述 Input Description###

    第一行 (c_1) ,(c_2) ,(c_3) ,(c_4) ,(tot) 下面(tot)(d_1) ,(d_2) ,(d_3) ,(d_4) ,(s) ,

    输出描述 Output Description###

    每次的方法数

    样例输入 Sample Input###

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

    样例输出 Sample Output###

    4
    27
    

    数据范围及提示 Data Size & Hint###

    其中(d_i,s<=100000,tot<=1000)

    之前的一些废话###

    退役的我回归了,但这次重返只是为了明天的比赛,时间虽短,但重新拾起这一切的我找到了5个月前为了梦想奋斗的自己,当时的自己热血沸腾,充满斗志,说真的,我无比热爱这种感觉,但这毕竟只是昙花一现,转瞬即逝的。

    题解###

    背包问题带有这么多附属条件,还有数量限制,看起来好复杂啊。(谁叫我几个月没有碰OI,大脑生锈了呢)
    但是如果不考虑数量限制,这就是一个很智障的背包问题了(md第一次打我循环顺序还打反了)
    (f_i)表示不考虑任何限制,由四种硬币组成价值为i的方案数,转移为:$f[i]=sum {f_{i-c_j}} (j leq 4) $
    显然这并不是最后的答案,因为这些方案里还藏着不少几类硬币超限的方案,于是我们需要用容斥原理把他们减掉
    ans=(f_s) -(只有第一类硬币超的方案)--(只有第二类硬币超的方案)-(只有第三类硬币超的方案)-(只有第四类硬币超的方案)+(只有第一类第二类硬币超的方案)+..+
    反正就是这四个集合的交加加减减
    现在考虑如何算(只有第一类硬币超的方案)。当第一类硬币去到(d_1+1) 时那么一定就超了,剩下的空间就可以随便分配了,所以这个方案数$ =f_{s-c_1*(d_1+1)}$
    其他的都类似。

    代码###

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #include<queue>
    #include<cstdlib>
    using namespace std;
    typedef long long LL;
    #define mem(a,b) memset(a,b,sizeof(a))
    typedef pair<int,int> PII;
    #define X first
    #define Y second
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    const int maxn=5;
    int c[maxn],d[maxn];
    LL dp[100010];
    int main()
    {
    	for(int i=0;i<4;i++)c[i]=read();
    	dp[0]=1ll;
    	for(int i=0;i<4;i++)
    		for(int j=c[i];j<=100000;j++)dp[j]+=dp[j-c[i]];
    	//for(int i=0;i<=10;i++)printf("DP:%d %d
    ",i,dp[i]);
    	//cout<<"fa"<<dp[100000]<<endl;
    	int T=read();
    	while(T--)
    	{
    		for(int i=0;i<4;i++)d[i]=read();
    		int s=read();
    		LL ans=0;
    		for(int i=0;i<16;i++)
    		{
    			int cnt=0;
    			LL tmp=0;
    			for(int j=0;j<4;j++)
    				if(i&(1<<j))cnt++,tmp+=(LL)c[j]*(LL)(d[j]+1);
    			if(tmp>s)continue;
    			if(cnt%2)ans-=dp[s-tmp];
    			else ans+=dp[s-tmp];
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    总结###

    大脑不再那么锈了

  • 相关阅读:
    Excel导出失败的提示
    C#中将一个引用赋值null的作用
    POJ2112Optimal Milking(二分法+floyd最短+网络流量)
    三年流水账
    OpenCV(C++接口)学习笔记1-图像读取、显示、保存
    thinkphp3.2 代码生成并点击验证码
    8.19! 今天我有18生日,点击阅读或顶部 尾随幸运的一天!生日知识!↓——【Badboy】
    如何系统地学习JavaScript
    HDU 3065 病毒在继续 (AC自己主动机)
    使用Canvas和Paint自己绘制折线图
  • 原文地址:https://www.cnblogs.com/FYH-SSGSS/p/8526127.html
Copyright © 2020-2023  润新知