zoukankan      html  css  js  c++  java
  • 洛谷P1450 [HAOI2008]硬币购物 动态规划 + 容斥原理

    洛谷P1450 [HAOI2008]硬币购物

    动态规划 + 容斥原理

    1、首先我们去掉限制 假设 能够取 无数次 也就是说一开始把他当做完全背包来考虑
    离线DP 预处理 复杂度 4*v

    用f[ i ] 表示 空间 为 i 的方案数
    答案ans 其实就是所有方案 - 所有超过限制的方案 限制指的就是题目中限制 某个硬币有几枚

    然后所有超过限制的方案用容斥来做

    所有超过限制的方案 要减 == -1 超过限制的方案 - 2 超过限制的方案 - 3 超过限制的方案 - 4 超过限制的方案
    + 1和2 超过限制的方案 +1和3超过限制的方案 + 1和4 超过限制的方案 ..... - 1和2和3超过限制的方案 .....
    + 1和2和3和4 超过限制的方案
    然后这样容斥就行了 因为只有四个数 相当于只要运算 16 次 就行 计算一次的复杂度为 O(1)

    总的时间复杂度 4*maxv + T*(1) 复杂度 O(v+T)

    ----------------------------------


    另外 来自 hzwer 的题解


    我想起了cf的某道题。。。
    dp预处理+容斥原理
    byvoid:
    设F[i]为不考虑每种硬币的数量限制的情况下,得到面值i的方案数。则状态转移方程为

    F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4}

    为避免方案重复,要以k为阶段递推,边界条件为F[0]=1,这样预处理的时间复杂度就是O(S)。

    接下来对于每次询问,奇妙的解法如下:根据容斥原理,答案为 得到面值S的超过限制的方案数 – 第1种硬币超过限制的方案数 – 第2种硬币超过限制的方案数 – 第3种硬币超过限制的方案数 – 第4种硬币超过限制的方案数 + 第1,2种硬币同时超过限制的方案数 + 第1,3种硬币同时超过限制的方案数 + …… + 第1,2,3,4种硬币全部同时超过限制的方案数。

    当第1种硬币超过限制时,只要要用到D[1]+1枚硬币,剩余的硬币可以任意分配,所以方案数为 F[ S – (D[1]+1)C[1] ],当且仅当(S – (D[1]+1)C[1])>=0,否则方案数为0。其余情况类似,每次询问只用问16次,所以询问的时间复杂度为O(1)。

     1 #include <cstdio>
     2 #include <cmath>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <string>
     6 #include <algorithm>
     7 #include <iomanip>
     8 #include <iostream> 
     9 #define ll long long 
    10 using namespace std ; 
    11 
    12 int T,v ; 
    13 int c[5],d[5]; 
    14 ll ans ;
    15 ll f[100011] ; 
    16 
    17 inline int read() 
    18 {
    19     char ch = getchar() ; 
    20     int x = 0 , f = 1 ; 
    21     while(ch<'0'||ch>'9') { if(ch=='-') f = -1 ; ch = getchar() ; } 
    22     while(ch>='0'&&ch<='9') { x = x*10+ch-48 ; ch = getchar() ; }
    23     return x*f ; 
    24 }
    25 
    26 inline void dfs(int x,int k,ll sum) 
    27 {
    28     if(sum < 0 ) return ; 
    29     if(x==5) 
    30     {
    31         if(k&1) 
    32             ans-=f[sum] ;  
    33         else 
    34             ans+=f[sum] ; 
    35         return ; 
    36     }
    37     dfs(x+1,k+1, sum-(d[x]+1)*c[x] ) ;       //    确保其  一定超出限制   保证  x  一定  超出 限制  
    38     dfs(x+1,k,sum) ; 
    39 }
    40 
    41 int main() 
    42 {
    43     for(int i=1;i<=4;i++) c[ i ] = read() ; 
    44     T = read() ;
    45     f[ 0 ] = 1 ;  
    46     for(int i=1;i<=4;i++) 
    47         for(int j=c[ i ];j<=100000;j++)     //  动归时 不要忘记边界   
    48             f[ j ]+=f[ j-c[ i ] ] ; 
    49     while(T--) 
    50     {
    51         for(int i=1;i<=4;i++) d[ i ] = read() ; 
    52         v = read() ; 
    53         ans = 0 ; 
    54         dfs( 1,0,v ) ; 
    55         printf("%lld
    ",ans) ;         
    56     } 
    57     return 0 ; 
    58 }
  • 相关阅读:
    模电电路分析
    正式答辩提问
    Dockerfile
    Docker常用命令
    docker镜像与容器
    Docker容器与容器数据
    docker命令自动安装
    Docker与虚拟机
    Java Lambda表达式 Stream
    Java Lambda表达式
  • 原文地址:https://www.cnblogs.com/third2333/p/7065318.html
Copyright © 2011-2022 走看看