2.3 记录结果再利用的“动态规划”
基础的动态规划算法
POJ 3176 从三角形顶端走到底边使经过的数字和最大
从下往上逆推答案
1 #include <cmath> 2 #include <iostream> 3 using namespace std; 4 5 int row[355][355]; 6 7 int main() { 8 int n; 9 scanf("%d", &n); 10 for (int i = 1; i <= n; i++) { 11 for (int j = 1; j <= i; j++) scanf("%d", &row[i][j]); 12 } 13 for (int i = n - 1; i >= 1; i--) { 14 for (int j = 1; j <= i; j++) 15 row[i][j] += max(row[i + 1][j], row[i + 1][j + 1]); 16 } 17 printf("%d\n", row[1][1]); 18 }
POJ 2229 问一个数拆成2的幂次之和有多少种拆法
考虑当前数字的组成方案中可能有几个"1",把这些1去掉之后,剩下的数(都是偶数)除以二就一一对应于 剩下的数 的和 对应的答案
另解:考虑当前数字的组成方案中是否有1,得出递推关系式 dp[i] = dp[i - 1] + dp[i >> 1](i为偶数)
1 #include <cstdio> 2 using namespace std; 3 4 const int mod = 1e9; 5 const int maxn = 1e6 + 5; 6 7 int a[maxn], s[maxn], x; 8 9 int main () { 10 a[0] = s[0] = 1; 11 for(int i = 1; i <= 1000000; i++) { 12 a[i] = s[i / 2]; 13 s[i] = (s[i - 1] + a[i]) % mod; 14 } 15 scanf("%d", &x); 16 printf("%d\n", a[x]); 17 }
POJ 2385 接苹果问题
关键:dp[i][j] 在第i分钟,cow移动了j次
1 #include <cstdio> 2 using namespace std; 3 4 int a[1005], even[1005], n, w; 5 int dp[1005][35]; 6 7 int max(int a, int b) { 8 return a > b ? a : b; 9 } 10 11 int main () { 12 scanf("%d%d", &n, &w); 13 for(int i = 1; i <= n; i++) { 14 scanf("%d", &a[i]); 15 even[i] = even[i - 1] + (a[i] == 1); 16 } 17 for(int i = 1; i<= n; i++) { 18 dp[i][0] = even[i]; 19 for(int j = 1; j <= w; j++) { 20 for(int k = 0; k <= i; k++) { 21 if(j & 1) dp[i][j] = max(dp[k][j - 1] + (i - k) - (even[i] - even[k]), dp[i][j]); 22 else dp[i][j] = max(dp[k][j - 1] + even[i] - even[k], dp[i][j]); 23 } 24 } 25 } 26 printf("%d\n", dp[n][w]); 27 }
1 #include <cstdio> 2 using namespace std; 3 4 int a[1005], n, w, ans; 5 int dp[1005][35]; 6 7 int max(int a, int b) { 8 return a > b ? a : b; 9 } 10 11 int main () { 12 scanf("%d%d", &n, &w); 13 for(int i = 1; i <= n; i++) { 14 scanf("%d", &a[i]); 15 } 16 for(int i = 1; i <= n; i++) { 17 dp[i][0] = dp[i - 1][0]; 18 for(int j = 1; j <= w; j++) { 19 dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]); 20 } 21 if (a[i] & 1) for(int j = 0; j <= w; j+=2) dp[i][j]++; 22 else for(int j = 1; j <= w; j+=2) dp[i][j]++; 23 } 24 for(int i = 0; i <= w; i++) ans = max(ans, dp[n][i]); 25 printf("%d\n", ans); 26 }
POJ 3616 给出N个区间及其权值,取不重复的若干区间使权值和最大
(这一题结束时刻是可以与起始时刻重合的)
考虑从时间上的转移或者从具体某一区间转移过来
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 using namespace std; 5 #define ll long long 6 7 const int maxn = 1e6 + 5; 8 9 int n, m, r; 10 ll dp[maxn]; 11 struct pii { 12 int s, e, val; 13 bool operator < (const pii &o) const { return e < o.e; } 14 } p[maxn]; 15 16 ll max(ll a, ll b) { 17 return a > b ? a : b; 18 } 19 20 int main () { 21 scanf("%d %d %d", &n, &m, &r); 22 for(int i = 0; i < m; i++) { 23 scanf("%d %d %d", &p[i].s, &p[i].e, &p[i].val); 24 p[i].e += r; 25 } 26 sort(p ,p + n); 27 for(int i = 0, t = 0; t <= n + r; t++) { 28 dp[t] = dp[t - 1]; 29 while(i < n && p[i].e == t) { 30 dp[t] = max(dp[p[i].s] + p[i].val, dp[t]); 31 i++; 32 } 33 } 34 printf("%lld\n", dp[n + r]); 35 }
1 #include <algorithm> 2 #include <cmath> 3 #include <iostream> 4 #include <cstdio> 5 using namespace std; 6 #define ll long long 7 8 ll dp[1005]; 9 10 struct inf { 11 int s, e, v; 12 } inter[1001]; 13 14 bool cmp(const inf &x, const inf &y) { return x.e < y.e; } 15 16 ll max(ll a, ll b) { 17 return a > b ? a : b; 18 } 19 20 int main() { 21 int n, m, r; 22 scanf("%d %d %d", &n, &m, &r); 23 for (int i = 0; i < m; i++) { 24 scanf("%d %d %d", &inter[i].s, &inter[i].e, &inter[i].v); 25 inter[i].e += r; 26 } 27 sort(inter, inter + m, cmp); 28 dp[0] = inter[0].v; 29 for (int i = 1; i < m; i++) { 30 inf t = {0, inter[i].s, 0}; 31 inf *ite = upper_bound(inter, inter + i, t, cmp); 32 if (ite == inter) 33 dp[i] = max(dp[i - 1], (ll)inter[i].v); 34 else 35 dp[i] = max(dp[i - 1], dp[ite - inter - 1] + inter[i].v); 36 } 37 printf("%lld\n", dp[m - 1]); 38 }
POJ 3280 给出删除和添加某字符的代价,对于给定字符串,求经过操作后的字符串是回文串最小代价
添加和删除是对称的操作,取其中的较小值即可。dp[i][j]代表从位置i到j的字串变成回文串需要的最小代价
1 #include <algorithm> 2 #include <cmath> 3 #include <iostream> 4 using namespace std; 5 6 int value[30], dp[2005][2005]; 7 int n, m, x, y; 8 string str; 9 char s; 10 11 int main() { 12 ios::sync_with_stdio(false); 13 cin >> n >> m >> str; 14 for (int i = 0; i < n; i++) { 15 cin >> s >> x >> y; 16 value[s - 'a'] = min(x, y); 17 } 18 for (int i = m - 1; i >= 0; i--) { 19 for (int j = i + 1; j < m; j++) { 20 if (str[i] == str[j]) { 21 dp[i][j] = dp[i + 1][j - 1]; 22 } else { 23 dp[i][j] = min(dp[i + 1][j] + value[str[i] - 'a'], 24 dp[i][j - 1] + value[str[j] - 'a']); 25 } 26 } 27 } 28 cout << dp[0][m - 1]; 29 }
优化递推关系式
POJ 1742 多重部分和问题
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 6 int dp[100005], res; 7 8 int a[105], c[105], n, m; 9 10 int main() { 11 while (scanf("%d %d", &n, &m) && n) { 12 memset(dp, -1, sizeof(dp)); 13 dp[0] = res = 0; 14 for (int i = 0; i < n; i++) scanf("%d", &a[i]); 15 for (int i = 0; i < n; i++) scanf("%d", &c[i]); 16 for (int i = 0; i < n; i++) { 17 for (int j = 0; j < a[i]; j++) 18 if (dp[j] >= 0) dp[j] = c[i]; 19 for (int j = a[i]; j <= m; j++) { 20 if (dp[j] == -1) { 21 if (dp[j - a[i]] <= 0) 22 dp[j] = -1; 23 else 24 dp[j] = dp[j - a[i]] - 1; 25 } else { 26 dp[j] = c[i]; 27 } 28 } 29 } 30 for (int i = 1; i <= m; i++) res += (dp[i] >= 0); 31 printf("%d\n", res); 32 } 33 }
POJ 3046 多重集组合数
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 6 const int mod = 1e6; 7 int res, dp[2][100005]; 8 int t, a, s, b, x; 9 int c[1005]; 10 11 int main() { 12 scanf("%d%d%d%d", &t, &a, &s, &b); 13 for (int i = 0; i < a; i++) { 14 scanf("%d", &x); 15 c[x]++; 16 } 17 dp[0][0] = dp[1][0] = 1; 18 for (int i = 1; i <= t; i++) { 19 for (int j = 1; j <= c[i]; j++) 20 dp[i & 1][j] = (dp[i & 1][j - 1] + dp[1 - i & 1][j]) % mod; 21 for (int j = c[i] + 1; j <= b; j++) 22 dp[i & 1][j] = (dp[i & 1][j - 1] + dp[1 - i & 1][j] - 23 dp[1 - i & 1][j - c[i] - 1] + mod) % 24 mod; 25 } 26 for (int i = s; i <= b; i++) res = (res + dp[t & 1][i]) % mod; 27 printf("%d\n", res); 28 }
POJ 3181 无数量限制的多重部分和+高精度
dp[i]不再表示当前物品用剩多少,而是方案数
1 #include <algorithm> 2 #include <cstring> 3 #include <iostream> 4 #define ll long long 5 using namespace std; 6 7 string dp[2][1005]; 8 9 string add(string a, string b) { 10 if (a.size() < b.size()) swap(a, b); 11 int f = 0; 12 for (int i = a.size() - 1, j = b.size() - 1; j >= 0; i--, j--) { 13 a[i] += b[j] - '0' + f; 14 if (a[i] > '9') { 15 a[i] -= 10; 16 f = 1; 17 } else 18 f = 0; 19 } 20 for (int i = a.size() - b.size() - 1; i >= 0 && f; i--) { 21 f = 0; 22 a[i]++; 23 if (a[i] > '9') { 24 a[i] -= 10; 25 f = 1; 26 } 27 } 28 if (f) a = "1" + a; 29 return a; 30 } 31 32 int main() { 33 int n, k; 34 cin >> n >> k; 35 dp[0][0] = "1"; 36 for (int i = 1; i <= k; i++) { 37 fill(dp[1], dp[1] + 1001, "0"); 38 for (int j = 0; j < i; j++) dp[1][j] = dp[0][j]; 39 for (int j = i; j <= n; j++) dp[1][j] = add(dp[1][j - i], dp[0][j]); 40 swap(dp[0], dp[1]); 41 } 42 cout << dp[0][n]; 43 }
需稍加思考的题目
POJ 1065 给出N个矩形,长度递增且宽度递增的一组矩形可以划在一起,求最小组数
贪心
1 #include <algorithm> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 6 struct inf { 7 int l, w; 8 } stick[5001]; 9 10 bool cmp(inf x, inf y) { return x.l < y.l; } 11 12 int vis[5005]; 13 14 int main() { 15 int t, n, x, y; 16 cin >> t; 17 while (t--) { 18 cin >> n; 19 for (int i = 0; i < n; i++) { 20 cin >> stick[i].l >> stick[i].w; 21 } 22 sort(stick, stick + n, cmp); 23 memset(vis, 0, sizeof(vis)); 24 int sum = -1, t; 25 do { 26 t = 0; 27 for (int i = 0; i < n; i++) { 28 if (!vis[i] && (t == 0 || t <= stick[i].w)) { 29 t = stick[i].w; 30 vis[i] = 1; 31 } 32 } 33 sum++; 34 } while (t); 35 cout << sum << "\n"; 36 } 37 }
POJ 1631 给出n组连线(二分图的形状),求其中最大的互不交叉的子集
以左边为基准时,子集对应的右边的序号应该是递增的。所以问题转化为最大上升子序列
1 #include <algorithm> 2 #include <cstdio> 3 #include <iostream> 4 using namespace std; 5 6 int c[40005]; 7 int dp[40005]; 8 9 int main() { 10 int n, a; 11 scanf("%d", &n); 12 while (n--) { 13 scanf("%d", &a); 14 for (int i = 0; i < a; i++) scanf("%d", c[i]); 15 fill(dp, dp + a, 0x7f7f7f7f); 16 for (int i = 0; i < a; i++) { 17 *lower_bound(dp, dp + a, c[i]) = c[i]; 18 } 19 printf("%d\n", lower_bound(dp, dp + a, 0x7f7f7f7f) - dp); 20 } 21 }
POJ 3666 给出一个序列,可以对某一个数字+1/-1的调整,最终使序列不增/不减。求最小代价(测试数据不严格,只要处理不减的情况就可以过)
好题目。dp[i][j]的意义是 使前i个数都降到比第j大的数小于或者等于 并且这个字串是不减的 的 代价
状态转移时,考虑前n个数,要么全比第j-1小,要么至少有一个数(第i个)变成第j大;前者即dp[i][j-1],后者是dp[i-1][j]+es[j]-a[i]
1 #include <algorithm> 2 #include <cstdio> 3 #include <iostream> 4 using namespace std; 5 6 #define ll long long 7 8 ll dp[2005]; 9 10 int n, a[2005], es[2005]; 11 12 ll min(ll a, ll b) { return a < b ? a : b; } 13 14 int main() { 15 scanf("%d", &n); 16 for (int i = 0; i < n; i++) { 17 scanf("%d", &a[i]); 18 es[i] = a[i]; 19 } 20 sort(es, es + n); 21 for (int i = 0; i < n; i++) { 22 for (int j = 0; j < n; j++) { 23 if (a[i] >= es[j]) 24 dp[j] += a[i] - es[j]; 25 else 26 dp[j] = min(dp[j - 1], dp[j] + es[j] - a[i]); 27 } 28 } 29 printf("%lld\n", dp[n - 1]); 30 }
POJ 2392 给出N种石头的高度,数量,最高能出现的高度。要求能垒成的最高高度
好题目。带限制的多重部分和
1 #include <algorithm> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <iostream> 6 using namespace std; 7 8 struct node { 9 int h, a, c; 10 bool operator<(const node &o) const { return a < o.a; } 11 } s[405]; 12 13 int dp[40005], k; 14 15 int main() { 16 scanf("%d", &k); 17 for (int i = 0; i < k; i++) scanf("%d %d %d", &s[i].h, &s[i].a, &s[i].c); 18 sort(s, s + k); 19 memset(dp, -1, sizeof(dp)); 20 dp[0] = 0; 21 for (int i = 0; i < k; i++) { 22 for (int j = 0; j < s[i].h; j++) 23 if (dp[j] >= 0) dp[j] = s[i].c; 24 for (int j = s[i].h; j <= s[i].a; j++) { 25 if (dp[j] >= 0) 26 dp[j] = s[i].c; 27 else if (dp[j - s[i].h] > 0) 28 dp[j] = dp[j - s[i].h] - 1; 29 else 30 dp[j] = -1; 31 } 32 } 33 for (int i = s[k - 1].a; i >= 0; i--) 34 if (dp[i] >= 0) { 35 printf("%d\n", i); 36 exit(0); 37 } 38 }
POJ 2184 N个元素,各有两个关键字;选取若干个使得每个关键字和非负且使总和尽可能大
有负值的背包问题(对两个关键字和DP时 常数是 对第二关键字DP 的2倍,会T)(因为OJ比较老,其实问题不大)
1 #include <algorithm> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 using namespace std; 7 8 const int shift = 1e5; 9 int dp[2][200005]; 10 11 int n, ts, tf, res; 12 13 int main() { 14 memset(dp[0], 128, sizeof(dp[0])); 15 memset(dp[1], 128, sizeof(dp[1])); 16 17 dp[0][shift] = 0; 18 19 scanf("%d", &n); 20 21 for (int i = 0; i < n; i++) { 22 scanf("%d %d", &ts, &tf); 23 if (ts <= 0 && tf <= 0) continue; 24 for (int j = max(0, ts); j <= (int)2e5 + min(0, ts); j++) { 25 dp[1][j] = max(dp[0][j], dp[0][j - ts] + tf); 26 } 27 for (int j = 0; j < max(0, ts); j++) dp[1][j] = dp[0][j]; 28 for (int j = (int)2e5 + min(0, ts) + 1; j <= (int)2e5; j++) 29 dp[1][j] = dp[0][j]; 30 swap(dp[0], dp[1]); 31 } 32 for (int i = shift; i <= (int)2e5; i++) { 33 if (dp[0][i] >= 0) res = max(res, i - shift + dp[0][i]); 34 } 35 printf("%d\n", res); 36 }
END
来源:https://www.cnblogs.com/hs-zlq/p/12193949.html