zoukankan      html  css  js  c++  java
  • AtCoder Beginner Contest 182 F

    F - Valid payments

    简化题意:有(n)种面值的货币,保证(a[1]=1,且a[i+1]是a[i]的倍数)
    有一个价格为(x)元的商品,付款(y)元,找零(y-x)元。
    问满足以下条件的情况下的应支付金额(y)有多少种?
    条件一:付款和找零都使用最少的硬币数量。
    条件二:在满足条件一的情况下,付款是用过的硬币面值,找零时无法使用。
    考虑这个题对于条件一而言,可以归结为第(i)个硬币食用的个数不能超过(a[i+1]/a[i]),否则的话我们直接使用(a[i+1])会更优。这样的话在条件一的情况下,每个金额(x)都有唯一的表示方法:(x=k_{x1}*a_1+k_{x2}*a_2+k_{x3}*a_3+...+k_{xn}*a_n),这样的话我们令(b=y-x),则对于条件二而言限制条件就变成了(k_{yi}和k_{bi})不能同时非零,即不能同时使用。
    这样的话我们可以列出以下等式:
    (x=k_{x1}*a_1+k_{x2}*a_2+...+k_{xn}*a_n)
    (+b=k_{b1}*a_1+k_{b2}*a_2+...+k_{bn}*a_n)
    (y=k_{y1}*a_1+k_{y2}*a_2+...+k_{yn}*a_n)
    限制条件如下:(k_i不能超过a[i+1]/a[i]),且(k_{yi})(k_{bi})不能同时非零。问不同的(y),或(b)的方案数?(可以发现(y)(b)是一一对应的。)
    由于(k_{yi})(k_{bi})不能同时非零,则至少一个为(0),所以我们直接分类讨论就行:
    1.若(k_{yi})为0,则说明我们这里(k_{xi})(k_{bi})相加的和必须也为0,但这怎么可能呢?考虑会不会是进位的问题呢?什么意思就是我的(k_{xi})(k_{bi})都不超过(a[i+1]/a[i])但我的和却超过它了,这样的话就可以用前面的替代了。那么这样的话也就是必须满足(k_{xi}+k_{bi}+上一位的进位=a[i+1]/a[i]).这样的话才能满足当前位为(0).
    2.若(k_{bi})为0,说明(x_{ki}+上一位的进位=y_{ki})
    也就是说只要我们知道上一位是否进位,我们就可以根据当前(x_{ki})来确定当前不同情况下的y和b了。具体的就是我们设(dp[i][0/1])表示前(i)位,并且第(i)位进位/不进位的方案数。
    则根据情况二有:(dp[i][0]+=dp[i-1][0],dp[i][0]+=dp[i-1][1](k_{xi}!=a[i+1]/a[i]-1))
    根据情况一有:(dp[i][1]+=dp[i-1][1],dp[i][1]+=dp[i-1][0](k_{xi}!=0))
    由于金额最大的没有限定的使用数量,但我们发现(k_{bn})只能为0.

    //不等,不问,不犹豫,不回头.
    #include<bits/stdc++.h>
    #define _ 0
    #define ls p<<1
    #define db double
    #define rs p<<1|1
    #define P 1000000007
    #define ll long long
    #define INF 1000000000
    #define get(x) x=read()
    #define PLI pair<ll,int>
    #define PII pair<int,int>
    #define ull unsigned long long
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define rep(x,y,z) for(int x=y;x<=z;++x)
    #define fep(x,y,z) for(int x=y;x>=z;--x)
    #define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
    using namespace std;
    const int N=110;
    ll n,x,a[N],mx[N],kx[N],dp[N][2];
    
    inline ll read()
    {
        ll x=0,ff=1;
        char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*ff;
    }
    
    int main()
    {
        //freopen("1.in","r",stdin);
    	get(n);get(x);
    	rep(i,1,n) get(a[i]);
    	rep(i,1,n-1) mx[i]=a[i+1]/a[i];
    	fep(i,n,1)
    	{
    		if(x>=a[i])
    		{
    			kx[i]=x/a[i];
    			x-=kx[i]*a[i];
    		}
    	}
    	dp[0][0]=1;
    	rep(i,1,n)
    	{
    		dp[i][0]+=dp[i-1][0];
    		dp[i][1]+=dp[i-1][1];
    		if(kx[i]!=mx[i]-1) dp[i][0]+=dp[i-1][1];
    		if(kx[i]!=0) dp[i][1]+=dp[i-1][0];
    	}
    	putl(dp[n][0]);
        return (0^_^0);
    }
    //以吾之血,铸吾最后的亡魂.
    
    
  • 相关阅读:
    CRM 客户线索 与 销售人员
    CRM X
    MySQL为Null导致的5大坑
    搞懂 macOS 上的主机名/hostname/ComputerName
    Node服务中如何写日志?
    Linux下 iptables 超详细教程和使用示例
    精读《Prisma 的使用》
    Redis夺命20问
    redis HyperLogLog的使用
    聊聊redis分布式锁的8大坑
  • 原文地址:https://www.cnblogs.com/gcfer/p/15242261.html
Copyright © 2011-2022 走看看