首页 > 代码库 > 洛谷P1450 [HAOI2008]硬币购物 动态规划 + 容斥原理
洛谷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\n",ans) ; 56 } 57 return 0 ; 58 }
洛谷P1450 [HAOI2008]硬币购物 动态规划 + 容斥原理