动规基础方程整理
背包
1.填满型01背包(装箱,递推方式+单调队列,后效性用倒着枚举避开)
1 2 3 4 5 6 7 8 9 10 | bool f[20001]; int n, a[31], v; scanf ( "%d%d" , &v, &n); f[0] = 1; for ( int i = 1; i <= n; ++i) { scanf ( "%d" , &a[i]); for ( int j = v; j >= a[i]; --j) f[j] |= f[j - a[i]]; } for ( int j = v; j >= 0; --j) if (f[j]) { printf ( "%d\n" , v - j); break ;} |
2.含价值的填满型01背包(改一下动规方程)
1 2 3 4 5 6 7 8 | scanf ( "%d%d" , &V, &n); for ( int i = 1; i <= n; ++i) { scanf ( "%d%d" , &cost, &val); for ( int j = V; j >= cost; --j) f[j] = max(f[j], f[j - cost] + val), ans = max(ans, f[j]); } printf ( "%d\n" , ans); |
3.含价值的填满型完全背包(改变枚举顺序)
1 2 3 4 5 6 7 8 | scanf ( "%d%d" , &V, &n); for ( int i = 1; i <= n; ++i) { scanf ( "%d%d" , &cost, &val); for ( int j = cost; j <= V; ++j) f[j] = max(f[j], f[j - cost] + val), ans = max(f[j], ans); } printf ( "%d\n" , ans); |
4.复杂的,有连锁关系的背包(金明,不改变方程,只添加循环和判断)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | scanf ( "%d%d" , &V, &m); for ( int i = 1; i <= m; ++i) { int v, p, q; scanf ( "%d%d%d" , &v, &p, &q); if (!q) { cost[++n][0] = v; weight[n][0] = p; ind[i] = n; } else { int zhu = ind[q]; cost[zhu][++fu[zhu]] = v; weight[zhu][fu[zhu]] = p; } } for ( int zhu = 1; zhu <= n; ++zhu) { for ( int j = V; j >= cost[zhu][0]; --j) { dp[j] = max(dp[j], dp[j - cost[zhu][0]] + cost[zhu][0] * weight[zhu][0]); if (fu[zhu] >= 1 && j >= cost[zhu][0] + cost[zhu][1]) dp[j] = max(dp[j], dp[j - cost[zhu][0] - cost[zhu][1]] + cost[zhu][0] * weight[zhu][0] + cost[zhu][1] * weight[zhu][1]); if (fu[zhu] >= 2 && j >= cost[zhu][0] + cost[zhu][2]) dp[j] = max(dp[j], dp[j - cost[zhu][0] - cost[zhu][2]] + cost[zhu][0] * weight[zhu][0] + cost[zhu][2] * weight[zhu][2]); if (fu[zhu] >= 2 && j >= cost[zhu][0] + cost[zhu][1] + cost[zhu][2]) dp[j] = max(dp[j], dp[j - cost[zhu][0] - cost[zhu][1] - cost[zhu][2]] + cost[zhu][0] * weight[zhu][0] + cost[zhu][1] * weight[zhu][1] + cost[zhu][2] * weight[zhu][2]); ans = max(ans, dp[j]); } } printf ( "%d\n" , ans); |
5.匹配型填满型完全背包(用几个单词匹配一句完整的话,和01的可达性求解相似,外重循环要枚举当前目标)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <stdio.h> #include <string.h> #include <iostream> using namespace std; char stand[6][10]={ { 'o' , 'n' , 'e' },{ 'p' , 'u' , 't' , 'o' , 'n' }, { 'o' , 'u' , 't' },{ 'o' , 'u' , 't' , 'p' , 'u' , 't' }, { 'i' , 'n' },{ 'i' , 'n' , 'p' , 'u' , 't' }}; int T, len, l[]={3,5,3,6,2,5}; char s[1000001]; bool f[1000001]; bool cmp( char *str, int leng, int pos) { for ( int i = 0; i < leng; ++i) if (str[i] != s[pos + i]) return false ; return true ; } bool jud() { len = strlen (s); f[0] = true ; for ( int i = 1; i <= len; ++i) f[i] = false ; for ( int j = 1; j <= len; ++j) for ( int i = 0; i < 6; ++i) if (j >= l[i] && cmp(stand[i], l[i], j - l[i])) f[j] |= f[j - l[i]]; return f[len]; } int main() { for ( scanf ( "%d" , &T); T; --T) { scanf ( "%s" , s); puts (jud() ? "YES" : "NO" ); } return 0; } |
6.一维一边推(美元与马克)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <stdio.h> #include <string.h> #include <iostream> using namespace std; int n; double f[103][2], x; int main() { scanf ( "%d" , &n); f[0][0] = 100.00; for ( int i = 1; i <= n; ++i) { scanf ( "%lf" , &x); x /= 100.00; f[i][0] = max(f[i - 1][0], f[i - 1][1] / x); f[i][1] = max(f[i - 1][1], f[i - 1][0] * x); } printf ( "%.2lf\n" , f[n][0]); return 0; } |
7.一维一边推(乘积最大,注意枚举的顺序)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include<iostream> using namespace std; int n,m; long long s,a[41][41],f[41][7]; int main(){ ios::sync_with_stdio( false ); cin>>n>>m>>s; for ( int i=n;i>=1;i--){ a[i][i]=s%10; s/=10; } for ( int i=2;i<=n;i++) for ( int j=i-1;j>=1;j--) a[j][i]=(a[j][i-1]<<1)+(a[j][i-1]<<3)+a[i][i]; for ( int i=1;i<=n;i++) f[i][0]=a[1][i]; for ( int k=1;k<=m;k++) for ( int i=k+1;i<=n;i++) for ( int j=k;j<i;j++) f[i][k]=max(f[i][k],f[j][k-1]*a[j+1][i]); cout<<f[n][m]<<endl; return 0; } |
8.二维一边推(最大公共子序列,LCS,利用后效性质进行递推)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdio.h> #include <string.h> #include <iostream> using namespace std; char s1[1001], s2[1001]; int l1, l2, f[1001][1001], ans; int main() { scanf ( "%s%s" , s1, s2); l1 = strlen (s1), l2 = strlen (s2); for ( int i = 0; i < l1; ++i) for ( int j = 0; j < l2; ++j) { if (i)f[i][j] = max(f[i][j], f[i - 1][j]); if (j)f[i][j] = max(f[i][j], f[i][j - 1]); if (i && j)f[i][j] = max(f[i][j], f[i - 1][j - 1]); if (s1[i] == s2[j]) f[i][j] = i && j ? max(f[i][j], f[i - 1][j - 1] + 1) : 1; ans = max(ans, f[i][j]); } printf ( "%d\n" , ans); return 0; } |
9.二维一边推(LCS拓展题,字符串距离或基因序列配对问题,注意处理前缀先和空格匹配的情况)
1 2 3 4 5 6 7 8 9 10 11 12 13 | scanf ( "%s%s%d" , s1 + 1, s2 + 1, &K); l1 = strlen (s1 + 1); l2 = strlen (s2 + 1); for ( int i = 1; i <= l1; ++i) f[i][0] = f[i - 1][0] + K; for ( int j = 1; j <= l2; ++j) f[0][j] = f[0][j - 1] + K; for ( int i = 1; i <= l1; ++i) for ( int j = 1; j <= l2; ++j) { f[i][j] = f[i - 1][j - 1] + min( abs (s1[i] - s2[j]), K + K); f[i][j] = min(f[i][j], f[i - 1][j] + K); f[i][j] = min(f[i][j], f[i][j - 1] + K); } printf ( "%d\n" , f[l1][l2]); |
10.中链式(能量项链,注意枚举顺序和扩展两倍的处理)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> #include <string.h> #include <iostream> using namespace std; int n; long long h[201], f[201][201]; int main() { scanf ( "%d" , &n); for ( int i = 1; i <= n; ++i) scanf ( "%lld" , &h[i]), h[i + n] = h[i]; for ( int k = 1; k < n; ++k) for ( int i = 1; i + k <= n + n; ++i) for ( int j = i + 1; j <= i + k; ++j) f[i][i + k] = max(f[i][i + k], f[i][j - 1] + f[j][i + k] + h[i] * h[j] * h[i + k + 1]); long long ans = 0; for ( int i = 1; i <= n; ++i) ans = max(ans, f[i][i + n - 1]); printf ( "%lld\n" , ans); return 0; } |
11.复杂中链式(最大算式,蓝桥杯。。。,注意开long long)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> #include <string.h> #include <iostream> #define oo 0x3f3f3f3f using namespace std; int n, K; long long f[20][20][20]; int main() { scanf ( "%d%d" , &n, &K); for ( int i = 1; i <= n; ++i) scanf ( "%lld" , &f[i][i][0]); for ( int m = 0; m <= K; ++m) for ( int k = m; k < n; ++k) for ( int i = 1; i + k <= n; ++i) for ( int j = i; j < i + k; ++j) for ( int mm = 0; mm <= m; ++mm) f[i][i + k][m] = max(f[i][i + k][m], max(m ? f[i][j][mm] * f[j + 1][i + k][m - mm - 1] : -oo, f[i][j][mm] + f[j + 1][i + k][m - mm])); printf ( "%lld\n" , f[1][n][K]); return 0; } |
12.非常规动规(筷子,带有贪心思想,可以简化很多状态,还是很好想的,f[i][k]代表前i根筷子组成k双的最小差平方之和)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; int n, K, t[101], f[101][60]; int main() { scanf ( "%d%d" , &n, &K); for ( int i = 1; i <= n; ++i) scanf ( "%d" , &t[i]); if (n / 2 < K + 3) puts ( "-1" ); else { memset (f, 127, sizeof f); sort(t + 1, t + 1 + n); f[0][0] = 0; for ( int k = 1; k <= K + 3; ++k) for ( int i = k * 2; i <= n; ++i) f[i][k] = min(f[i - 1][k], f[i - 2][k - 1] + (t[i] - t[i - 1]) * (t[i] - t[i - 1])); printf ( "%d\n" , f[n][K + 3]); } return 0; } |
13.非常规动规(不重叠线段,要求覆盖线段不重叠时最长的覆盖长度,用f[l]表示覆盖到l为止得到的最大覆盖长,明显是无后效性的,还有算法本身是还有优化空间的,但本题不需要)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; struct seg { int l, r; bool operator < ( const seg other) const { return l < other.l;} }s[1001]; int n, f[2001], ans; int main() { scanf ( "%d" , &n); for ( int i = 1; i <= n; ++i) scanf ( "%d%d" , &s[i].l, &s[i].r); sort(s + 1, s + 1 + n); for ( int i = 1; i <= n; ++i) for ( int j = 0; j < s[i].l; ++j) f[s[i].r] = max(f[s[i].r], f[j] + s[i].r - s[i].l + 1); for ( int j = 0; j <= 2000; ++j) ans = max(f[j], ans); printf ( "%d\n" , ans); return 0; } |
14.非常规动规(观光游览,连续区间划分问题,注意状态是否已经达到,是否可以用来转移必须用数组标记,具体题目具体分析很重要,很多时候出错就是这种问题)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <stdio.h> #include <string.h> #include <iostream> using namespace std; inline void read( int &x) { int c = getchar (); x = 0; while (c < '0' || c > '9' ) c = getchar (); while (c >= '0' && c <= '9' ) x = x * 10 + c - 48, c = getchar (); } int m, n, K, c[101][101], f[101][101]; int main() { read(m), read(n); memset (c, 0, sizeof c); for ( int i = 1; i <= n; ++i) { int x, y, v; read(x), read(y), read(v); for ( int l = 1; l <= x; ++l) for ( int r = y; r <= m; ++r) c[l][r] += v; } read(K); memset (f, 128, sizeof f); f[0][0] = 0; for ( int j = 1; j <= m; ++j) for ( int k = 1; k <= K; ++k) for ( int jj = 0; jj < j; ++jj) f[j][k] = max(f[j][k], f[jj][k - 1] + c[jj + 1][j]); printf ( "%d\n" , f[m][K]); return 0; } |
15.火车票(坐火车,注意的要点和上一题一样)(题目传送门)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <stdio.h> #include <string.h> #include <iostream> using namespace std; int L1, L2, L3, C1, C2, C3, n, dis[101], s, t, f[101]; int P( int d) { if (d <= L1) return C1; if (d <= L2) return C2; return C3; } int main() { scanf ( "%d%d%d%d%d%d%d%d%d" , &L1, &L2, &L3, &C1, &C2, &C3, &n, &s, &t); for ( int i = 2; i <= n; ++i) scanf ( "%d" , &dis[i]); if (s > t) swap(s, t); memset (f, 126, sizeof f); f[s] = 0; for ( int i = s + 1; i <= t; ++i) for ( int j = i - 1; j >= s; --j) { if (dis[i] - dis[j] > L3) break ; f[i] = min(f[i], f[j] + P(dis[i] - dis[j])); } printf ( "%d\n" , f[t]); return 0; } |
路径输出
1.三维LCS路径输出,使用递归,其实也可以用循环啦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <stdio.h> #include <string.h> #include <iostream> using namespace std; char s1[101], s2[101], s3[101], path[101]; int l1, l2, l3, f[101][101][101]; int main() { scanf ( "%s%s%s" , s1 + 1, s2 + 1, s3 + 1); l1 = strlen (s1 + 1), l2 = strlen (s2 + 1), l3 = strlen (s3 + 1); for ( int i = 1; i <= l1; ++i) for ( int j = 1; j <= l2; ++j) for ( int k = 1; k <= l3; ++k) { f[i][j][k] = max(f[i - 1][j][k], f[i][j - 1][k]); f[i][j][k] = max(f[i][j][k], f[i][j][k - 1]); f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k - 1] + (s1[i] == s2[j] && s2[j] == s3[k])); } printf ( "%d\n" , f[l1][l2][l3]); int i = l1, j = l2, k = l3, l = 0; while (f[i][j][k]) { if (f[i][j][k] == f[i][j - 1][k]) --j; else if (f[i][j][k] == f[i - 1][j][k]) --i; else if (f[i][j][k] == f[i][j][k - 1]) --k; else path[++l] = s1[i], --i, --j, --k; } while (l) putchar (path[l--]); return 0; } |
优化思想
1.单调队列队头优化(坐电梯,注意枚举顺序是从上到下,点这里进入题目)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include <stdio.h> #include <string.h> #include <iostream> const double oo = 0x3f3f3f3f; using namespace std; inline void read( int &x) { int c = getchar (); while (c < '0' || c > '9' ) c = getchar (); while (c >= '0' && c <= '9' ) x = x * 10 + c - 48, c = getchar (); } double f[10001], cur[201]; int n, m, l[201], r[201]; inline double calc( double a, double b) { return (a*(a + 1) + b*(b + 1)) / 2 / (a + b + 1);} int main() { read(n), read(m); for ( int i = 1; i <= n; ++i) read(l[i]), read(r[i]); fill(cur + 1, cur + 1 + n, oo); for ( int i = 1; i <= n; ++i) if (r[i] == m) cur[i] = calc(m - l[i], 0); for ( int j = m - 1; j >= 1; --j) { f[j] = oo; for ( int i = 1; i <= n; ++i) if (l[i] <= j && j <= r[i]) f[j] = min(f[j], cur[i]); for ( int i = 1; i <= n; ++i) if (l[i] <= j && j <= r[i]) cur[i] = min(cur[i], f[j] + calc(r[i] - j, j - l[i])); } printf ( "%.5lf\n" , f[1] + m - 1); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)