动态规划(DP)

// 以下题目来自牛客网

删括号

f[i][j][k] 表示序列s的前i个匹配序列t的前j个,序列s删除部分左括号与右括号数量差为k的情况是否可行

答案为 f[sl][tl][0]

状态转移:

当 f[i][j][k] 可行时

  1. s[i+1]==t[j+1] 且 k==0 则 f[i+1][j+1][k] = 1
  2. s[i+1]=='('  则s串删去当前括号可匹配,即 f[i+1][j][k+1] = 1
  3. s[i+1]==')'  则 k>0 时s串多删去一个左括号匹配,即 f[i+1][j][k-1] = 1
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5.  
  6. bool f[][][]; //s前i个删去括号能否成为t前j个,左右括号差为k
  7. char s[], t[];
  8. int main() {
  9. scanf("%s", s+);
  10. scanf("%s", t+);
  11. int sl = strlen(s+), tl = strlen(t+);
  12. f[][][] = ;
  13.  
  14. for(int i=;i<sl;i++) {
  15. for(int j=;j<tl;j++) {
  16. for(int k=;k<sl;k++) if(f[i][j][k]) {
  17. if(k== && s[i+]==t[j+]) f[i+][j+][k] = ;
  18. if(s[i+]=='(') f[i+][j][k+] = ;
  19. else if(k) f[i+][j][k-] = ;
  20. }
  21. }
  22. }
  23. printf("%s\n", f[sl][tl][]?"Possible":"Impossible");
  24. return ;
  25. }

回文子序列计数

错误思路:x[i] = 左右26个小写字母选取0~min(l[i][j], r[i][j]) (0<=j<26)的组合数之积。

正确求法:见代码。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. const int mod = 1e9+;
  6. typedef long long ll;
  7.  
  8. ll x[], dp[]; // dp[i]位回文子序列个数
  9. // dp[i+1]
  10. char s[];
  11. int main()
  12. {
  13. scanf("%s", s);
  14. int n = strlen(s);
  15. for(int i=;i<n;i++) x[i] = ;
  16. for(int i=;i<n;i++) {
  17. ll sum = , tmp;
  18. for(int j=n-;j>i;j--) {
  19. tmp = dp[j];
  20. if(s[j]==s[i-]) {
  21. dp[j] = (dp[j] + sum + ) % mod;
  22. }
  23. sum = (sum + tmp) % mod;
  24. x[i] = (x[i] + dp[j]) % mod;
  25. }
  26. }
  27.  
  28. ll ans = ;
  29. for(int i=;i<n;i++) {
  30. ans = ans^((i+) * x[i]) % mod;
  31. }
  32. printf("%lld\n", ans);
  33. return ;
  34. }

牛牛的计算机内存

状压dp

直接 dp[22][1<<20] 会MLE,只能用滚动数组记录状态。

int dp[1<<20];     // dp[S]: 前i条指令状态为S的最小代价
int state[1<<20]; // state[i]:j 指令状态i执行完后的内存状态为j

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. const int INF = 0x3f3f3f3f;
  7. int dp[<<]; // dp[S]: 前i条指令,访问完状态为S的最小代价
  8. int state[<<]; // state[i]:S 前i条指令执行完状态为S
  9. int a[];
  10.  
  11. int main() {
  12.  
  13. memset(dp, INF, sizeof(dp));
  14. dp[] = ;
  15.  
  16. int n, m;
  17. char ins[];
  18. scanf("%d %d", &n, &m);
  19. for(int i=;i<n;i++) {
  20. scanf("%s", ins);
  21. int k = ;
  22. for(int j=;j<m;j++) {
  23. a[i] = a[i]* + (ins[j]-'');
  24. if(ins[j]=='') ++k;
  25. }
  26. state[<<i] = a[i];
  27. dp[<<i] = k*k;
  28. }
  29.  
  30. for(int S=;S<(<<n);S++) {
  31. if(dp[S]==INF) continue;
  32.  
  33. for(int i=;i<n;i++) {
  34. if((S>>i)&) continue;
  35.  
  36. int nexS = S|(<<i), k = ;
  37. for(int j=;j<m;j++) {
  38. if((a[i]>>j)& && ((state[S]>>j)&)==) {
  39. ++k;
  40. }
  41. }
  42. if(dp[nexS]>dp[S]+k*k) {
  43. dp[nexS] = dp[S] + k*k;
  44. state[nexS] = state[S]|a[i];
  45. }
  46. }
  47. }
  48.  
  49. printf("%d\n", dp[(<<n)-]);
  50. return ;
  51. }

棋盘的必胜策略

可以用 f[i][j][step] 记录到 mp[i][j] 用了step步的胜负状态,dfs即可。

  1. 如果下一步有必败态,当前则为必胜态
  2. 否则当前为必败态
  3. mp[i][j]终点为必败态
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5.  
  6. const int dx[] = {, , , -};
  7. const int dy[] = {, -, , };
  8.  
  9. int r, c, k;
  10. char mp[][];
  11. int f[][][];
  12.  
  13. bool check(int x, int y) {
  14. if(x<||y<||x>=r||y>=c)
  15. return false;
  16. if(mp[x][y]=='#')
  17. return false;
  18. return true;
  19. }
  20.  
  21. int dfs(int x, int y, int k) {
  22. if(f[x][y][k]!=-)
  23. return f[x][y][k];
  24. if(mp[x][y]=='E') // 走到终点,无法移动,必败
  25. return f[x][y][k] = ;
  26. if(k==)
  27. return ; // 走不了,必败
  28.  
  29. for(int i=;i<;i++) {
  30. int nx = x + dx[i];
  31. int ny = y + dy[i];
  32. if(check(nx, ny) && dfs(nx, ny, k-)==)
  33. return f[x][y][k] = ;
  34. }
  35. return f[x][y][k] = ;
  36. }
  37.  
  38. int main() {
  39. cin>>r>>c>>k;
  40. for(int i=; i<r; i++)
  41. scanf("%s",mp[i]);
  42. memset(f, -, sizeof(f));
  43.  
  44. int sx, sy;
  45. for(int i=;i<r;i++) {
  46. for(int j=;j<c;j++) {
  47. if(mp[i][j] == 'T') {
  48. sx = i;
  49. sy = j;
  50. }
  51. }
  52. }
  53.  
  54. printf("%s\n", dfs(sx, sy, k)?"niuniu":"niumei");
  55. return ;
  56. }

看起来像博弈论,其实分析一下最多走两步就能确定胜负,不用搜索状态也能解决。

分析见代码。

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4.  
  5. const int dx[] = {, , , -};
  6. const int dy[] = {, -, , };
  7.  
  8. int r, c, k;
  9. char mp[][];
  10. bool check(int x, int y) {
  11. if(x<||y<||x>=r||y>=c)
  12. return false;
  13. if(mp[x][y]=='#')
  14. return false;
  15. return true;
  16. }
  17. bool win(int x, int y) {
  18. for(int i=;i<;i++) {
  19. int nx = x + dx[i];
  20. int ny = y + dy[i];
  21. if(check(nx, ny) && mp[nx][ny]=='E')
  22. return true;
  23. }
  24. return false;
  25. }
  26. int main() {
  27. cin>>r>>c>>k;
  28. for(int i=; i<r; i++)
  29. scanf("%s",mp[i]);
  30.  
  31. int sx, sy;
  32. for(int i=;i<r;i++) {
  33. for(int j=;j<c;j++) {
  34. if(mp[i][j] == 'T') {
  35. sx = i;
  36. sy = j;
  37. }
  38. }
  39. }
  40.  
  41. bool f = false; // 第一步能否走
  42. for(int i=;i<;i++) {
  43. int nx = sx + dx[i];
  44. int ny = sy + dy[i];
  45. if(check(nx, ny)) {
  46. f = true;
  47. if(mp[nx][ny]=='E')
  48. return * printf("niuniu\n");
  49. }
  50. }
  51. if(!f) { // 动不了
  52. return * printf("niumei\n");
  53. }
  54. if(k==) { // 只走一步
  55. return * printf("niuniu\n");
  56. }
  57. if(k%==) { // 偶数步,往返走,走后必胜
  58. return * printf("niumei\n");
  59. }
  60. // 奇数步,第二步无法胜,第三步开始往返走,先走必胜
  61. for(int i=;i<;i++) {
  62. int nx = sx + dx[i];
  63. int ny = sy + dy[i];
  64. if(check(nx, ny) && mp[nx][ny]=='.' && !win(nx, ny)) {
  65. return * printf("niuniu\n");
  66. }
  67. }
  68. puts("niumei");
  69. return ;
  70. }

牛牛与数组

状态转移很好写,记录一下前缀和,减去dp[i-1][j] j的整数倍的部分即为dp[i][j]

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4. const int mod = 1e9+;
  5. int dp[][];
  6. int main() {
  7. int n, k;
  8. scanf("%d %d", &n, &k);
  9.  
  10. for(int i=;i<=k;i++) dp[][i] = ;
  11.  
  12. for(int i=;i<=n;i++) {
  13. int sum = ;
  14. for(int j=;j<=k;j++)
  15. sum = (sum + dp[i-][j]) % mod;
  16.  
  17. for(int j=;j<=k;j++) {
  18. int sum1 = ;
  19. for(int l=*j;l<=k;l+=j) {
  20. sum1 = (sum1 + dp[i-][l])% mod;
  21. }
  22. dp[i][j] = ((sum - sum1)%mod+mod)%mod;
  23. }
  24.  
  25. }
  26.  
  27. printf("%d\n", dp[n][k]);
  28. return ;
  29. }

牛牛去买球

n个盒子,每个盒子有a[i]个红球,b[i]个篮球,但a[i],b[i]有正负1的偏差,总和不变。买每个盒子的费用为c[i],求买k个相同的球的最小花费。

三种情况

  1. 买k个红球,每个盒子都当做a[i]-1个红球
  2. 买k个蓝球,每个盒子都当做b[i]-1个蓝球
  3. 买2k-1个球,至少保证有k个相同颜色的球

用滚动数组上限为最多的球数,而不是k。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6.  
  7. int dp[];
  8. int a[], b[], c[];
  9. int main() {
  10. int n, k; cin>>n>>k;
  11. for(int i=;i<=n;i++)
  12. scanf("%d", &a[i]);
  13. for(int i=;i<=n;i++)
  14. scanf("%d", &b[i]);
  15. for(int i=;i<=n;i++)
  16. scanf("%d", &c[i]);
  17.  
  18. int ans = 0x3f3f3f3f, up = ;
  19. memset(dp, 0x3f, sizeof(dp));
  20. dp[] = ;
  21. for(int i=;i<=n;i++) {
  22. int v = a[i] - ;
  23. for(int j=up;j>=v;j--) {
  24. dp[j] = min(dp[j], dp[j-v]+c[i]);
  25. }
  26. }
  27. for(int i=k;i<=*k;i++) ans = min(ans, dp[i]);
  28.  
  29. memset(dp, 0x3f, sizeof(dp));
  30. dp[] = ;
  31. for(int i=;i<=n;i++) {
  32. int v = b[i] - ;
  33. for(int j=up;j>=v;j--) {
  34. dp[j] = min(dp[j], dp[j-v]+c[i]);
  35. }
  36. }
  37. for(int i=k;i<=*k;i++) ans = min(ans, dp[i]);
  38.  
  39. memset(dp, 0x3f, sizeof(dp));
  40. dp[] = ;
  41. for(int i=;i<=n;i++) {
  42. int v = a[i]+b[i];
  43. for(int j=up;j>=v;j--) {
  44. dp[j] = min(dp[j], dp[j-v]+c[i]);
  45. }
  46. }
  47. for(int i=*k-;i<=up;i++) ans = min(ans, dp[i]);
  48. if(ans==0x3f3f3f3f) ans = -;
  49. printf("%d\n", ans);
  50. return ;
  51. }

小明打联盟

有3个小技能一个大招,大招的伤害值随时间线性变化。给定T时间,以及各个技能的释放时间和伤害值,问最大的伤害值是多少。

不考虑大招的话,就是多重背包问题。

把一个大招看成两个L, R时刻释放的大招d, e,中间时刻释放只会用一次。 (假设用两次m时刻的大招可以转化为大招e +  (2m-l)时刻的大招,还是相当于用一次)

然后再枚举L,R区间的最大伤害值即可。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5.  
  6. int t;
  7. int v[];
  8. int w[];
  9. long long dp[];
  10. int main() {
  11. while(scanf("%d", &t)!=EOF) {
  12. for(int i=;i<;i++) {
  13. scanf("%d %d", &v[i], &w[i]);
  14. }
  15. int L, R, temp, A;
  16. scanf("%d %d %d %d", &L, &R, &temp, &A);
  17. v[] = L; w[] = temp;
  18. v[] = R; w[] = temp + A*(R-L);
  19.  
  20. memset(dp, , sizeof(dp));
  21. for(int i=;i<;i++) {
  22. for(int j=v[i];j<=t;j++) { // 多重背包
  23. dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
  24. }
  25. }
  26. for(int j=L;j<=R;j++) {
  27. dp[t] = max(dp[t], dp[t-j]+temp+1LL*A*(j-L));
  28. }
  29.  
  30. printf("%lld\n", dp[t]);
  31. }
  32.  
  33. return ;
  34. }

树形dp

// 以下题目来自洛谷

P1352 没有上司的舞会

状态转移方程很简单,1A

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>
  4. #include<algorithm>
  5. using namespace std;
  6.  
  7. int n, fa[];
  8. int w[];
  9. vector<int> G[];
  10.  
  11. int dp[][];
  12. // dp[u][0] u没有参加
  13. // dp[u][1] u参加
  14.  
  15. void dfs(int u, int fa) {
  16. dp[u][] = w[u];
  17. for(int i=;i<G[u].size();i++) {
  18. int v = G[u][i];
  19. if(v==fa) continue;
  20.  
  21. dfs(v, u);
  22. dp[u][] += dp[v][];
  23. dp[u][] += max(dp[v][], dp[v][]);
  24. }
  25. }
  26.  
  27. int main() {
  28. scanf("%d", &n);
  29. for(int i=;i<=n;i++)
  30. scanf("%d", &w[i]);
  31.  
  32. int u, v;
  33. for(int i=;i<n;i++) {
  34. scanf("%d %d", &u, &v);
  35. G[u].push_back(v);
  36. G[v].push_back(u);
  37. fa[u] = v;
  38. }
  39.  
  40. int rt = -;
  41. for(int i=;i<=n;i++)
  42. if(!fa[i]) {
  43. rt = i;
  44. break;
  45. }
  46. dfs(rt, -);
  47. printf("%d\n", max(dp[rt][], dp[rt][]));
  48. return ;
  49. }

P2016 战略游戏

选出一棵树上最少的节点,能覆盖所有边。

这题结构跟上面类似,每一点放/不放两个状态。

查看题解有大佬指出这是最小点覆盖问题,使用匈牙利算法,对于无向图答案为 ans / 2 。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>
  4. #include<algorithm>
  5. using namespace std;
  6. const int maxn = ;
  7.  
  8. vector<int> G[maxn];
  9. int n;
  10. int f[maxn][];
  11.  
  12. void dfs(int u, int fa) {
  13. f[u][] = ;
  14. for(int i=;i<G[u].size();i++) {
  15. int v = G[u][i];
  16. if(v==fa) continue;
  17.  
  18. dfs(v, u);
  19.  
  20. f[u][] += f[v][];
  21. f[u][] += min(f[v][], f[v][]);
  22. }
  23. }
  24.  
  25. int main() {
  26. scanf("%d", &n);
  27. for(int i=;i<n;i++) {
  28. int u, v, k;
  29. scanf("%d %d", &u, &k);
  30. while(k--) {
  31. scanf("%d", &v);
  32. G[u].push_back(v);
  33. G[v].push_back(u);
  34. }
  35.  
  36. }
  37. dfs(, -);
  38. printf("%d\n", min(f[][], f[][]));
  39. return ;
  40. }

P2015 二叉苹果树

保留K条边苹果树上的最大苹果数量。

注意子树边的数量写法:dfs儿子后 sz[u] += sz[v] + 1;

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>
  4. #include<algorithm>
  5. using namespace std;
  6. const int maxn = ;
  7.  
  8. int n, K;
  9. struct Edge {
  10. int to, w;
  11. Edge(int v, int ww):to(v), w(ww){}
  12. };
  13. vector<Edge> G[maxn];
  14. int sz[maxn];
  15. int dp[maxn][maxn];
  16. // dp[u][i] : 以u为根的子树保留i条边的最多苹果数量
  17.  
  18. void dfs(int u, int fa) {
  19. for(int i=;i<G[u].size();i++) {
  20. int v = G[u][i].to;
  21. if(v==fa) continue;
  22.  
  23. dfs(v, u);
  24. sz[u] += sz[v] + ; // 边的数量
  25.  
  26. for(int j=min(sz[u], K);j>=;j--) { // 01背包,逆序
  27. for(int k=;k<=min(sz[v], j-);k++) {
  28. dp[u][j] = max(dp[u][j], dp[u][j-k-] + dp[v][k] + G[u][i].w);
  29. }
  30. }
  31.  
  32. }
  33. }
  34.  
  35. int main() {
  36. scanf("%d %d", &n, &K);
  37.  
  38. int u, v, w;
  39. for(int i=;i<n;i++) {
  40. scanf("%d %d %d", &u, &v, &w);
  41. G[u].push_back(Edge(v, w));
  42. G[v].push_back(Edge(u, w));
  43. }
  44.  
  45. dfs(, -);
  46. printf("%d\n", dp[][K]);
  47. return ;
  48. }

P2014 选课

课程之间有依赖关系,求选M门课程的最大学分。

将没有直接先修课的课程连在根为 0 的树上,从节点 0 dfs 即可。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>
  4. #include<algorithm>
  5. using namespace std;
  6. const int maxn = ;
  7.  
  8. int n, K;
  9. vector<int> G[maxn];
  10. int sz[maxn], w[maxn];
  11. int dp[maxn][maxn];
  12. // dp[u][i] : 以u为根的子树选i门课的最大学分
  13.  
  14. void dfs(int u) {
  15. sz[u] = ;
  16. for(int i=;i<G[u].size();i++) {
  17. int v = G[u][i];
  18.  
  19. dfs(v);
  20. sz[u] += sz[v];
  21.  
  22. for(int j=min(sz[u], K);j>=;j--) {
  23. for(int k=;k<=min(j-, sz[v]);k++) {
  24. dp[u][j] = max(dp[u][j], dp[u][j-k-] + dp[v][k]);
  25. }
  26. }
  27.  
  28. }
  29. }
  30.  
  31. int main() {
  32. scanf("%d %d", &n, &K);
  33.  
  34. int fa;
  35. for(int i=;i<=n;i++) {
  36. scanf("%d %d", &fa, &w[i]);
  37. G[fa].push_back(i);
  38. }
  39.  
  40. for(int i=;i<=n;i++) dp[i][] = w[i];
  41. dfs();
  42. printf("%d\n", dp[][K]);
  43. return ;
  44. }

P1270 “访问”美术馆

读入采用dfs形式给出美术馆的通过走廊的时间和藏画数量,问T时间内能盗窃多少幅画。

坑点:时间有效时间为 T - 1

记搜 / 树形dp 。由于要返回根节点,时间可以直接乘以 2 读入。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>
  4. #include<algorithm>
  5. using namespace std;
  6. const int maxn = ;
  7.  
  8. int T, tot;
  9. struct node {
  10. int cost, val;
  11. }tree[maxn*];
  12. int dp[maxn*][];
  13.  
  14. void dfs(int u, int t) {
  15. if(dp[u][t] || t==) return; // 0为0直接返回
  16.  
  17. if(tree[u].val) { // 根节点
  18. dp[u][t] = min(tree[u].val, (t-tree[u].cost)/);
  19. return;
  20. }
  21.  
  22. for(int i=;i<=t-tree[u].cost;i++) {
  23. dfs(u*, i);
  24. dfs(u*+, t-i-tree[u].cost); // 右边剩下时间= t - i - 2倍走廊时间
  25.  
  26. dp[u][t] = max(dp[u][t], dp[u*][i]+dp[u*+][t-i-tree[u].cost]);
  27.  
  28. }
  29. }
  30.  
  31. void build(int rt) {
  32. scanf("%d %d", &tree[rt].cost, &tree[rt].val);
  33. tree[rt].cost *= ;
  34. if(!tree[rt].val) {
  35. build(rt*);
  36. build(rt*+);
  37. }
  38. }
  39.  
  40. int main() {
  41.  
  42. scanf("%d", &T);
  43. build();
  44.  
  45. dfs(, T-);
  46.  
  47. printf("%d\n", dp[][T-]);
  48. return ;
  49. }

数位DP

// 以下来自洛谷

P2657 [SCOI2009]windy数

求A,B区间内满足相邻两位数字之差大于等于2的整数个数。

注意是在 !lim && !zero 条件下记忆化,没加这个条件调了半天。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<cstring>
  5. using namespace std;
  6. typedef long long ll;
  7.  
  8. ll dp[][]; // dp[i][j]:长度为i中最高位是j的windy数的个数
  9. int bit[];
  10. ll dfs(int pos, int lim, int last, int zero) {
  11. if(pos<) return ;
  12.  
  13. if(!lim && !zero && dp[pos][last]!=-) return dp[pos][last];
  14.  
  15. int res = ;
  16. int up = lim?bit[pos]:;
  17. for(int i=;i<=up;i++) {
  18. if(abs(i-last)<) continue;
  19.  
  20. res += dfs(pos-, lim&&(i==up), zero&&(i==)?:i, zero&&(i==));
  21. }
  22. if(!lim && !zero) dp[pos][last] = res;
  23. return res;
  24. }
  25.  
  26. ll cal(ll x) {
  27. int cnt = ;
  28. while(x) {
  29. bit[cnt++] = x%;
  30. x /= ;
  31. }
  32. memset(dp, -, sizeof(dp));
  33. return dfs(cnt-, , , );
  34. }
  35.  
  36. int main() {
  37. ll A, B;
  38.  
  39. while(cin>>A>>B)
  40.  
  41. printf("%lld\n", cal(B)-cal(--A));
  42.  
  43. return ;
  44. }

洛谷题解翻到别人的代码处理:

dp练习集的更多相关文章

  1. dp合集 广场铺砖问题&&硬木地板

    dp合集 广场铺砖问题&&硬木地板 很经典了吧... 前排:思想来自yali朱全民dalao的ppt百度文库免费下载 后排:STO朱全民OTZ 广场铺砖问题 有一个 W 行 H 列的广 ...

  2. 9.15 DP合集水表

    9.15 DP合集水表 显然难了一些啊. 凸多边形的三角剖分 瞄了一眼题解. 和蛤蛤的烦恼一样,裸的区间dp. 设f[i][j]表示i~j的点三角剖分最小代价. 显然\(f[i][i+1]=0,f[i ...

  3. 9.14 DP合集水表

    9.14 DP合集水表 关键子工程 在大型工程的施工前,我们把整个工程划分为若干个子工程,并把这些子工程编号为 1. 2. --. N:这样划分之后,子工程之间就会有一些依赖关系,即一些子工程必须在某 ...

  4. 数位dp题集

    题集见大佬博客 不要62 入门题,检验刚才自己有没有看懂 注意一些细节. 的确挺套路的 #include<bits/stdc++.h> #define REP(i, a, b) for(r ...

  5. TYVJ1071 LCIS 线性DP+决策集优化

    问题描述 TYVJ1071 题解 暴力\(\mathrm{DP}\) 首先,一个\(O(n^3)\)的解法: 设\(opt_{i,j}\)代表\(a\)的前\(i\)个和\(b\)的前\(j\)个的\ ...

  6. 杭电dp题集,附链接还有解题报告!!!!!

    Robberies 点击打开链接 背包;第一次做的时候把概率当做背包(放大100000倍化为整数):在此范围内最多能抢多少钱  最脑残的是把总的概率以为是抢N家银行的概率之和- 把状态转移方程写成了f ...

  7. 【CJOJ2498】【DP合集】最长上升子序列 LIS

    题面 Description 给出一个 1 ∼ n (n ≤ 10^5) 的排列 P 求其最长上升子序列长度 Input 第一行一个正整数n,表示序列中整数个数: 第二行是空格隔开的n个整数组成的序列 ...

  8. CJOJ 【DP合集】最长上升序列2 — LIS2

    题面 已知一个 1 ∼ N 的排列的最长上升子序列长度为 K ,求合法的排列个数. 好题(除了我想不出来我应该找不到缺点), 想一想最长上升子序列的二分做法, 接在序列后面或者替换. 所以对于每一个位 ...

  9. DP 题集 2

    关于 DP 的一些题目 String painter 先区间 DP,\(dp[l][r]\) 表示把一个空串涂成 \(t[l,r]\) 这个子串的最小花费.再考虑 \(s\) 字符串,\(f[i]\) ...

  10. DP 题集 1

    关于 DP 的一些题目 参考资料 [Tutorial] Non-trivial DP Tricks and Techniques DP Rain and Umbrellas Mr. Kitayuta, ...

随机推荐

  1. JavaScript学习总结(七)——ECMAScript6(ES6)

    一.ECMAScript概要 ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通 ...

  2. TCP/IP协议,,OSI的七层参考模型,HTTP请求响应机制

    一.TCP/IP协议 TCP/IP是Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是In ...

  3. 2019.2.23VScode的c++配置详细方法

    根据个人经验,最新的c++配置方法. 主要的步骤: 安装Vscode 在Vscode类安装c++插件 安装编译调试环境 修改Vscode配置文件. 安装Vscode的步骤省略 如何配置Vscode中文 ...

  4. laravel sql mode only_full_group_by 解决小记

    環境: mysql: 5.7.* Laravel: 5.4.* sql 中使用到了 group by,會提示 500錯誤,將 config/database.php中的 strict的值改爲true, ...

  5. day18 函数定义、参数;名称空间;全局变量及局部变量。

    Python之路,Day6 = Python基础6 函数的定义 def func1(): # 定义一个函数,名字叫func1,括号中没有传入参数 print('hello word') # 这里是 f ...

  6. 广义欧拉降幂(欧拉定理)——bzoj3884,fzu1759

    广义欧拉降幂对于狭义欧拉降幂任然适用 https://blog.csdn.net/qq_37632935/article/details/81264965?tdsourcetag=s_pctim_ai ...

  7. Oracle连接字符串总结

    Oracle XE 标准连接 Oracle XE(或者"Oracle Database 10g Express Edition")是一个简单免费发布的版本. 以下是语法格式: Dr ...

  8. JS继承(简单理解版)

    童鞋们,我们今天聊聊js的继承,关于继承,平时开发基本用不到,但是面试没有不考的,我就想问,这是人干的事吗? 好吧,迫于社会主义核心价值观,我们今天就来简单说一说js的继承,谁让它是面向对象编程很重要 ...

  9. (4)mysql约束

    完整性约束 1.设置非空约束 not null 简称 NK约束 创建表时设置非空 CREATE TABLE tb_meng2(id INT NOT NULL,age INT); 2.设置字段默认值 默 ...

  10. 阿里云CentOs7上安装Mysql

    前提:虽然yum源上有mysql,但是好像没有mysql-server,所以我们还是选择自己从官网上下载安装 一.新建文件夹,然后下载解压 cd /usr/ #新建mysql文件夹 mkdir mys ...