暑假集训 || 动态规划
Posted pinkglightning
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假集训 || 动态规划相关的知识,希望对你有一定的参考价值。
·背包问题:
有很多的变式
HDU 2546
即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)
首先拿出5元买最贵的东西,那接下来就是背包容量m-5,物品数量 n-1 的01背包问题了
if(m<5) cout<<m<<endl; else { int f[1010];//f[j]表示买前i件物品,预算为j时的最大花销 memset(f,0,sizeof(f)); for(int i=1; i<=n-1; i++) for(int j=m-5; j>=price[i]; j--) if(f[j]<f[j-price[i]]+price[i]) f[j]=f[j-price[i]]+price[i]; cout<<m-max-f[m-5]<<endl; }
CodeForces 544C
n个程序员要写m行程序,每个程序员在写一行程序时会产生ai个bug,写完m行共产生<=b个bug共有多少种方法
完全背包问题f[i][j][k]表示第i个程序员写了j行产生k个bug的方法时
f[i][j][k] = f[i-1][j][k] + f[i-1][j-1][k-a[i]] (考虑这个程序员这一行写或者不写)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int f[550][550], a[550]; int main() { int n, m, b, mod; scanf("%d %d %d %d", &n, &m, &b, &mod); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); f[0][0] = 1; for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) for(int k = a[i]; k <= b; k++) f[j][k] = (f[j][k] + f[j-1][k-a[i]]) % mod; int ans = 0; for(int i = 0; i <= b; i++) ans = (ans + f[m][i]) % mod; printf("%d ", ans); return 0; }
POJ 1837
一个臂长15的天平,在不同位置有钩子,钩子上可以挂砝码,给你G个砝码,要把所有砝码都挂在钩子上,一共有多少种挂法
f[i][j]表示挂了i个砝码,天平状态为j时的情况数,j=7500时表示平衡,j<7500向左倾斜,j>7500向右倾斜
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; typedef long long LL; #define SZ 20100 int c[33], g[33]; int f[33][SZ]; int main() { int C, G; scanf("%d %d", &C, &G); for(int i = 0; i < C; i++) scanf("%d", &c[i]); for(int i = 1; i <= G; i++) scanf("%d", &g[i]); memset(f, 0, sizeof(f)); for(int i = 0; i < C; i++) f[1][7500 + g[1]*c[i]] = 1; int ans = 0; for(int i = 2; i <= G; i++) for(int j = 0; j < 15000; j++) for(int k = 0; k < C; k++) if(f[i-1][j] != 0) f[i][j + g[i]*c[k]] += f[i-1][j]; printf("%d ", f[G][7500]); return 0; }
HDU 4901
题意:一个序列,取两个集合A和B,使得A集合中所有元素在B集合的左边,并且A集合中所有元素的异或值等于B集合中所有元素的and值,求满足条件的集合数量
f[i][j]为前i个数异或值为j的方案数。g[i][j]为后i个数and值为j的方案数
考虑到在中间的分割点可能重复选择,dp[i][j]表示后i个数且第i个数一定被选择的方案数
然后乘法原理
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long LL; #define SZ 1010 const LL mod = 1e9+7; LL f[SZ][1030], g[SZ][1030], dp[SZ][1030]; int a[SZ]; int main() { int T; scanf("%d", &T); while(T--) { int n; scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 0; i <= n; i++) for(int j = 0; j <= 1024; j++) f[i][j] = g[i][j] = dp[i][j] = 0; for(int i = 1; i < n; i++) { f[i][a[i]]++; for(int j = 0; j <= 1024; j++) if(f[i-1][j]) { f[i][j^a[i]] = (f[i][j^a[i]] + f[i-1][j]) % mod; f[i][j] = (f[i][j] + f[i-1][j]) % mod; } } for(int i = n; i > 1; i--) { g[i][a[i]]++; dp[i][a[i]]++; for(int j = 0; j <= 1024; j++) if(g[i+1][j])//到i+1位时的方案数,i+1位可取可不取 { g[i][j] = (g[i][j] + g[i+1][j]) % mod;//到i位时的方案数,i位不取 g[i][j&a[i]] = (g[i][j&a[i]] + g[i+1][j]) % mod;//到i位时的方案数,i位取 dp[i][j&a[i]] = (dp[i][j&a[i]] + g[i+1][j]) % mod;//到i位时的方案数,i位必须取 } } LL ans = 0; for(int i = 1; i < n; i++) for(int j = 0; j <= 1024; j++) ans = (ans + f[i][j] * dp[i+1][j]) % mod; printf("%lld ", ans); } return 0; }
以上是关于暑假集训 || 动态规划的主要内容,如果未能解决你的问题,请参考以下文章