文章目录

  • Maximum sum(求两个不重叠子区间最大和)
  • Post Office(感觉题解有问题)
  • 最长上升子序列
  • 最大子矩阵
  • 采药(0-1背包)
  • 最长公共子序列
  • 吃糖果
  • 登山
  • 最长公共上升子序列**(记录路径)
  • Exchange Rates
  • 移动路线
  • 摘花生
  • 数字组合
  • 糖果(模k 0-1背包)
  • 判断整除(模k 0-1背包)
  • 最大上升子序列
  • 怪盗基德的滑翔伞
  • 宠物小精灵之收服(二维背包)
  • 采方格
  • 开餐馆
  • 买书
  • 带通配符的字符串匹配
  • 放苹果
  • 最低通行费
  • 三角形的最佳路径
  • 鸡蛋的硬度
  • 大盗阿福
  • 切割回文
  • 乘积最大
  • 装箱问题
  • 方格取数
  • 滑雪
  • 核电站问题
  • 酒鬼
  • Pku2440 DNA
  • 奶牛散步
  • [Usaco2009 Feb]Bullcow
  • Logs Stacking堆木头

Maximum sum(求两个不重叠子区间最大和)

一、题目大意:

二、解题思路:
(1): 所求的两个子序列必定以一个数为分割点。
(2): 左边的子序列必定以分割点或者左边的其中一个数为结尾,右边的子序列必定以分割点右边的其中一个数为起始。

  • 定义状态:

    • qian[i]qian[i]qian[i]: 以a[i]a[i]a[i]为结尾的最大连续子序列和。
    • hou[i]hou[i]hou[i]: 以a[i]a[i]a[i]为起始的最大连续子序列和。
    • total1[i]total1[i]total1[i]: [0,i][0,i][0,i]区间内的最大连续子序列和。
    • total2[i]total2[i]total2[i]: [i,n−1][i,n-1][i,n−1]区间内的最大连续子序列和。
  • 目标状态: max({total1[i]+total2[i+1]∣0&lt;=i&lt;n})max(\{total1[i]+total2[i+1]\quad | \quad 0&lt;=i&lt;n\})max({total1[i]+total2[i+1]∣0<=i<n})
  • 状态转移:
    • qian[i]=max(a[i],qian[i−1]+a[i])qian[i] = max(a[i],qian[i-1]+a[i])qian[i]=max(a[i],qian[i−1]+a[i])
    • hou[i]=max(a[i],hou[i+1]+a[i])hou[i] = max(a[i], hou[i+1]+a[i])hou[i]=max(a[i],hou[i+1]+a[i])
    • total1[i]=max({qian[k]∣0&lt;=k&lt;=i})total1[i] = max(\{qian[k]\quad|\quad 0&lt;=k&lt;=i\})total1[i]=max({qian[k]∣0<=k<=i})
    • total2[i]=max({hou[k]∣i&lt;=k&lt;=n−1})total2[i] = max(\{hou[k]\quad|\quad i&lt;=k&lt;=n-1\})total2[i]=max({hou[k]∣i<=k<=n−1})
  • 初始状态:
    • qian[0]=a[0]qian[0] = a[0]qian[0]=a[0],
    • hou[n−1]=a[n−1]hou[n-1] = a[n-1]hou[n−1]=a[n−1]
  • 复杂度:O(n)O(n)O(n)

三、代码:

#include<iostream>
using namespace std;const int MAX = 50000+5;
const int inf = 1 << 29;
int T;
int main()
{int qian[MAX], hou[MAX], total1[MAX], total2[MAX];int a[MAX];cin >> T;for(int i=0; i<T; i++){int n;cin >> n;for(int j=0; j<n; j++)cin >> a[j];qian[0] = a[0];for(int j=1; j<n; j++)qian[j] = max(a[j], qian[j-1]+a[j]);hou[n-1] = a[n-1];for(int j=n-2; j>=0; j--)hou[j] = max(a[j], hou[j+1]+a[j]);total1[0] = qian[0];for(int j=1; j<n; j++)total1[j] = max(total1[j-1], qian[j]);total2[n-1] = hou[n-1];for(int j=n-2; j>=0; j--)total2[j] = max(total2[j+1], hou[j]);int ans = -inf;for(int j=0; j<n-1; j++)ans = max(ans, total1[j]+total2[j+1]);cout << ans << endl;}return 0;
}

Post Office(感觉题解有问题)

一、题目大意:

二、解题思路

  • 定义:

    • dp[i][j]dp[i][j]dp[i][j]:前iii个村庄建jjj个邮局的最小距离.
    • m[i][j]m[i][j]m[i][j]:村庄[i,j][i,j][i,j]之间建立一个邮局的最小距离.(想象: 很明显应该建在(i+j)/2(i+j)/2(i+j)/2的邮局上)
  • 目标状态: dp[V][P]dp[V][P]dp[V][P].即前VVV个村庄建立PPP个邮局.
  • 状态转移:
    • 子问题:

      • 定义:build(s,e,j):=build(s,e,j):=build(s,e,j):=在村庄[s,e][s,e][s,e] 间建jjj个邮局
      • build(1,i,j):=⋃k=1i−1{build(1,k,j−1)∩build(k+1,i,1)}build(1,i,j):=\bigcup_{k=1}^{i-1} \{build(1,k , j-1)\cap build(k+1, i, 1)\}build(1,i,j):=⋃k=1i−1​{build(1,k,j−1)∩build(k+1,i,1)}
    • 因此有: 状态转移方程:dp[i][j]=max{dp[k][j−1]+m[k+1][i]∣1&lt;=k&lt;=i−1}dp[i][j] = max\{dp[k][j-1]+m[k+1][i]\quad | \quad 1&lt;=k&lt;=i-1\}dp[i][j]=max{dp[k][j−1]+m[k+1][i]∣1<=k<=i−1}(这里默认了每一个子问题下,最优情况为: 前kkk个村庄只往前j−1j-1j−1个邮局前进,如果是要往第jjj个邮局,这种情况包含在其他子问题下)因此状态转移最终结果没有问题。
    • m[i][j]=m[i][j−1]+a[j]−a[(i+j)/2]m[i][j] = m[i][j-1]+a[j]-a[(i+j)/2]m[i][j]=m[i][j−1]+a[j]−a[(i+j)/2](想象可得出)
  • 转移策略: 先求出所有的mmm, dp[i][j]dp[i][j]dp[i][j]只依赖与dp[i′][j−1]dp[i'][j-1]dp[i′][j−1],因此只需循环更新下三角矩阵。
  • 初始状态:dp[i][1]=m[1][i],dp[i][0]=infdp[i][1] = m[1][i], dp[i][0]=infdp[i][1]=m[1][i],dp[i][0]=inf

三、 代码:

#include<iostream>
using namespace std;
const int MAXV = 304;
const int MAXP = 34;
const int inf = 1 << 30;
int dp[MAXV][MAXP];
int m[MAXV][MAXV];
int a[MAXV];
int main()
{int V, P;cin >> V >> P;for(int i=1; i<=V; i++)cin >> a[i];for(int i=0; i<=V; i++)for(int j=0; j<=P; j++)dp[i][j] = inf;for(int i=1; i<=V; i++)m[i][i] = 0;for(int i=1; i<=V; i++)for(int j=i+1; j<=V; j++)m[i][j] = m[i][j-1] + a[j] - a[(i+j)/2];for(int i=1; i<=V; i++)dp[i][1] = m[1][i];for(int i=2; i<=V; i++)for(int j=1; j<=i; j++)for(int k=1; k<=i-1; k++)dp[i][j] = min(dp[i][j], dp[k][j-1]+m[k+1][i]);cout << dp[V][P] << endl;return 0;
}

最长上升子序列

会议状态转移、初态。题解略, 代码

#include<iostream>
using namespace std;
const int MAXN = 1005;
int a[MAXN];
int main()
{int n;cin >> n;for(int i=1; i<=n; i++)cin >> a[i];int dp[MAXN];dp[1] = 1;for(int i=2; i<=n; i++){dp[i] = 1;for(int j=1; j<=i-1; j++){if(a[i] > a[j])dp[i] = max(dp[i], dp[j]+1);}}int ans = 0;for(int i=1; i<=n; i++)ans = max(ans, dp[i]);cout << ans << endl;return 0;
}

最大子矩阵

一、题目大意

二、解题思想
1、利用sum[i][j]记录(i,j)左上方所有的和,然后利用前缀和的思想快速求出某个矩阵的和。最后使用四重循环,但是超时。
2、使用压缩维度和动态规划思想。
(1)压缩维度

  • 定义sumOfLine[i][j]sumOfLine[i][j]sumOfLine[i][j]:为第jjj列上前iii行所有数的和。使用O(n2)O(n^2)O(n2)求出结果。

(2)动态规划:对任意一个横跨[i,j][i,j][i,j]行的矩阵,我们使用sumOfLinesumOfLinesumOfLine快速将其压缩为一维,然后求这个一维数组的最大连续子序列和。任意两行组合的最大值即为最终答案。O(n3)O(n^3)O(n3)

三、代码

#include<iostream>
#include<stdio.h>
using namespace std;
const int MAX = 105;
const int inf = 1 << 30;
int grad[MAX][MAX];
int sum_of_line[MAX][MAX];
int line[MAX];
int get_max_sequence(int n)
{int dp[MAX];dp[1] = line[1];for(int i=2; i<=n; i++)dp[i] = max(line[i], dp[i-1]+line[i]);int res = -inf;for(int i=1; i<=n; i++)res = max(dp[i], res);return res;
}
int main()
{int n;scanf("%d", &n);for(int i=1; i<=n; i++)for(int j=1; j<=n; j++)scanf("%d", &grad[i][j]);// ---------------计算行前缀和-----------------------------for(int j=1; j<=n; j++)for(int i=1; i<=n; i++)sum_of_line[i][j] = sum_of_line[i-1][j] + grad[i][j];// --------------------------------------------------------------int ans = -inf;for(int r1=1; r1<=n; r1++){for(int r2=r1; r2<=n; r2++){for(int j=1; j<=n; j++)line[j] = sum_of_line[r2][j] - sum_of_line[r1-1][j];int line_max = get_max_sequence(n);ans = max(line_max, ans);}}cout << ans << endl;return 0;}

采药(0-1背包)

#include<stdio.h>
using namespace std;const int MAXT = 1005;
const int MAXN = 105;
int dp[2][MAXT];
int time[MAXN];
int value[MAXN];
int max(int a, int b)
{if(a < b)return b;return a;
}
int main()
{int T, M;scanf("%d%d", &T, &M);for(int i=1; i<=M; i++)scanf("%d%d", &time[i], &value[i]);for(int i=0; i<=T; i++)dp[0][i] = 0;for(int i=1; i<=M; i++){for(int j=1; j<=T; j++){if(j-time[i] >= 0)dp[i%2][j] = max(dp[1-i%2][j], dp[1-i%2][j-time[i]] + value[i]);elsedp[i%2][j] = dp[1-i%2][j];}}printf("%d\n", dp[M%2][T]);return 0;
}

最长公共子序列

基本dp,回忆状态转移,代码:

#include<iostream>
using namespace std;
const int MAX = 205;int dp[MAX][MAX];int main()
{string s1, s2;while(cin >>s1 >> s2){int l1 = s1.length();int l2 = s2.length();for(int i=0; i<=max(l1, l2); i++){dp[0][i] = 0;dp[i][0] = 0;}for(int i=1; i<=l1; i++){for(int j=1; j<=l2; j++){if(s1[i-1] == s2[j-1])dp[i][j] = dp[i-1][j-1] + 1;elsedp[i][j] = max(dp[i-1][j], dp[i][j-1]);}}cout << dp[l1][l2] << endl;}return 0;
}

吃糖果

定义:dp[i]dp[i]dp[i],还剩iii块的吃法
代码:

#include<iostream>
using namespace std;
typedef long long ll;
ll dp[25];
int main()
{dp[1] = 1;dp[2] = 2;int N;cin >> N;for(int i=3; i<=N; i++)dp[i] = dp[i-1]+dp[i-2];cout << dp[N] << endl;return 0;
}

登山

一、题目大意

二、解题思想:
最优路线肯定以其中一个点为峰值点,因此求从左到右和从右到左的最长上升子序列
三、代码:

#include<iostream>
using namespace std;const int MAXN = 1005;
int dp1[MAXN];
int dp2[MAXN];
int main()
{int n;cin >> n;int a[MAXN];for(int i=1; i<=n; i++)cin >> a[i];dp1[1] = 1;for(int i=2; i<=n; i++){dp1[i] = 1;for(int j=1; j<i; j++){if(a[j] < a[i])dp1[i] = max(dp1[i], dp1[j]+1);}}dp2[n]=1;for(int i=n-1; i>=0; i--){dp2[i] = 1;for(int j=n; j>i; j--){if(a[i] > a[j])dp2[i] = max(dp2[i], dp2[j] + 1);}}int ans = -1;for(int i=1; i<=n; i++)ans = max(ans, dp1[i] + dp2[i] - 1);cout << ans << endl;return 0;
}

最长公共上升子序列**(记录路径)

Exchange Rates

一、题目大意
有两种货币US / Canada Dollor, 每天给出两种货币的汇率,然后可以选择全部换或者不换,换的时候要缴纳0.03的手续费(换之后),并且两位小数后的钱直接不要,刚开始时有1000 Canada Dollor,问给出N天的Canada DollorUS Dollor的汇率,最后第N天最多可以有多少Canada Dollor
二、解题思路

  • 定义状态dp[i][k],k∈{0,1}dp[i][k], k\in\{0,1\}dp[i][k],k∈{0,1}:当k=1k=1k=1时,为第iii天最多可拥有的us dollor, k=0k=0k=0为第iii天最多可拥有的can dollor.
  • 目标状态: dp[N][0]dp[N][0]dp[N][0]
  • 状态转移方程: dp[i][0]=max(dp[i−1][0],dp[i−1][1]∗a[i]∗0.97)dp[i][0]=max(dp[i-1][0], dp[i-1][1]*a[i]*0.97)dp[i][0]=max(dp[i−1][0],dp[i−1][1]∗a[i]∗0.97)dp[i][1]=max(dp[i−1][1],dp[i−1][0]/a[i]∗0.97)dp[i][1] = max(dp[i-1][1], dp[i-1][0]/a[i]*0.97)dp[i][1]=max(dp[i−1][1],dp[i−1][0]/a[i]∗0.97)
  • 初态:dp[0][0]=1000,dp[0][1]=0dp[0][0] = 1000, dp[0][1]=0dp[0][0]=1000,dp[0][1]=0

三、代码

#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;
const int MAXD = 366;
double dp[MAXD][2];
double a[MAXD];
int N;
double remove_cent(double x)
{return floor(x * 100)*1.0 / 100;
}
int main()
{while(cin >> N && N){for(int i=1; i<=N; i++)cin >> a[i]; // 指a[i] Canada dollor  <----> 1 US dollor, 即换出的时候应该越小越好,换进的时候越大越好dp[0][0] = 1000;dp[0][1] = 0;for(int i=1; i<=N; i++){dp[i][0] = max(dp[i-1][0], remove_cent(dp[i-1][1]*a[i]*0.97));dp[i][1] = max(dp[i-1][1], remove_cent(dp[i-1][0]/a[i]*0.97));}// cout << dp[N][0] << endl;printf("%.2lf\n",dp[N][0]);}return 0;
}

移动路线

一、题目大意

二、解题思路

  • 定义dp[i][j]:dp[i][j]:dp[i][j]:从第(i,j)(i,j)(i,j)个格子到终点的路线数量。
  • 目标状态dp[1][1]dp[1][1]dp[1][1]:即从起点出发的路线数量。
  • 状态转移方程:dp[i][j]=dp[i+1][j]+dp[i][j+1]dp[i][j] = dp[i+1][j]+dp[i][j+1]dp[i][j]=dp[i+1][j]+dp[i][j+1]
  • 初态dp[m][n]=1,dp[0][∗]=dp[∗][0]=0dp[m][n]=1, dp[0][*]=dp[*][0]=0dp[m][n]=1,dp[0][∗]=dp[∗][0]=0
  • 更新方式,采用记忆化搜索方便写代码

三、代码

#include<iostream>
#include<cstring>
using namespace std;typedef long long ll;
ll dp[25][25];
int get_num(int x, int y)
{if(dp[x][y] >= 0)return dp[x][y];dp[x][y] = get_num(x+1, y) + get_num(x, y+1);return dp[x][y];
}
int main()
{int n, m;cin >> n >> m;memset(dp, -1, sizeof(dp));for(int i=0; i<25; i++)dp[n+1][i] = dp[i][m+1] = 0;dp[n][m] = 1;cout << get_num(1,1) << endl;return 0;}

摘花生

一、题目大意

二、解题思路

  • 定义: dp[i][j]:dp[i][j]:dp[i][j]:从(i,j)(i,j)(i,j)出发到终点过程中最多还能采摘的花生数。
  • 目标:dp[1][1]dp[1][1]dp[1][1]
  • 状态转移: dp[i][j]=grad[i][j]+max(dp[i+1][j],dp[i][j+1])dp[i][j] = grad[i][j]+max(dp[i+1][j], dp[i][j+1])dp[i][j]=grad[i][j]+max(dp[i+1][j],dp[i][j+1])
  • 初态:dp[c][r]=1,dp[c+1][∗]=dp[∗][r+1]=0dp[c][r]=1, dp[c+1][*] = dp[*][r+1] = 0dp[c][r]=1,dp[c+1][∗]=dp[∗][r+1]=0
  • 更新方式,采用记忆化搜索。

三、代码

#include<iostream>
#include<cstring>
using namespace std;
const int MAXM = 105;
typedef long long ll;
ll dp[MAXM][MAXM];
int grad[MAXM][MAXM];ll get_num(int x, int y)
{if(dp[x][y] >= 0)return dp[x][y];dp[x][y] = grad[x][y] + max(get_num(x+1, y), get_num(x, y+1));return dp[x][y];
}
int main()
{int r, c;int T;cin >> T;while(T--){cin >> r >> c;for(int i=1;i<=r; i++){for(int j=1; j<=c; j++){cin >> grad[i][j];}}memset(dp, -1, sizeof(dp));for(int i=0; i<MAXM; i++)dp[r+1][i] = dp[i][c+1] = 0;dp[r][c] = grad[r][c];ll ans = get_num(1, 1);cout << ans << endl;}return 0;
}

数字组合

一、题目大意

二、解题思想

  • 定义dp[i][j]dp[i][j]dp[i][j]:前iii个数组成jjj的方式数量。
  • 目标dp[n][t]dp[n][t]dp[n][t].
  • 状态转移:dp[i][j]={dp[i−1][j]+dp[i−1][j−a[i]]j−a[i]&gt;=0dp[i−1][j]elsedp[i][j] = \begin{cases} dp[i-1][j] + dp[i-1][j-a[i]]&amp; j - a[i]&gt;=0\\ dp[i-1][j] &amp; else \end{cases}dp[i][j]={dp[i−1][j]+dp[i−1][j−a[i]]dp[i−1][j]​j−a[i]>=0else​
    初始状态: dp[0][∗]=0,dp[0][0]=1dp[0][*] = 0, dp[0][0]=1dp[0][∗]=0,dp[0][0]=1

三、代码

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 25;
const int MAXT = 1005;
typedef long long ll;
ll dp[MAXN][MAXT];
int a[MAXN];
int main()
{int n, t;cin >> n >> t;for(int i=1; i<=n; i++)cin >> a[i];for(int i=0; i<=t; i++)dp[0][i] = 0;dp[0][0] = 1;for(int i=1; i<=n; i++){for(int j=0; j<=t; j++){if(j-a[i] >= 0){dp[i][j] = dp[i-1][j] + dp[i-1][j-a[i]];}elsedp[i][j] = dp[i-1][j];}}cout << dp[n][t] << endl;return 0;
}

糖果(模k 0-1背包)

一、题目大意

二、解题思想
这里的TTT实在太大了,如果直接用0-1背包,会超时,这类题给出了倍数的概念,那么就要往取模上考虑了。

  • 定义dp[i][j]:dp[i][j]:dp[i][j]:前iii个物品中选择物品使得重量和模k等于jjj的最大重量。
  • 目标状态dp[n][0]dp[n][0]dp[n][0].
  • 状态转移:
    • 子问题: 对第iii个物品来说,依然有选或者不选两种选择。

      • 不选:很明显dp[i][j]=dp[i−1][j]dp[i][j] = dp[i-1][j]dp[i][j]=dp[i−1][j]
      • 选:dp[i][j]=dp[i−1][((j−a[i])%k+k)%k]+a[i]dp[i][j] = dp[i-1][((j-a[i])\%k+k)\%k] + a[i]dp[i][j]=dp[i−1][((j−a[i])%k+k)%k]+a[i]
    • 状态转移方程:
      dp[i][j]=max(dp[i−1][j],dp[i−1][((j−a[i])%k+k)%k]+a[i])dp[i][j] = max(dp[i-1][j], dp[i-1][((j-a[i])\%k+k)\%k]+a[i])dp[i][j]=max(dp[i−1][j],dp[i−1][((j−a[i])%k+k)%k]+a[i])
  • 初态: dp[0][∗]=−inf,dp[0][0]=0dp[0][*]=-inf, dp[0][0]=0dp[0][∗]=−inf,dp[0][0]=0

三、代码

#include<iostream>
using namespace std;const int MAXM = 100+5;
const int inf = 1<<30;
typedef long long ll;
ll dp[MAXM][MAXM];
ll a[MAXM];
int main()
{int n,k;cin >> n >> k;for(int i=1;i<=n; i++)cin >> a[i];for(int i=0; i<k; i++)dp[0][i] = -inf;dp[0][0] = 0;for(int i=1; i<=n;i++){for(int j=0; j<k; j++){dp[i][j] = max(dp[i-1][j], dp[i-1][((j-a[i])%k+k)%k]+a[i]);}}cout << dp[n][0] << endl;return 0;
}

判断整除(模k 0-1背包)

一、题目大意

二、解题思路

  • 定义: dp[i][j]∈{0,1}:dp[i][j]\in\{0,1\}:dp[i][j]∈{0,1}:前iii个数组合模kkk是否能够等于jjj.
  • 目标状态: dp[n][0]dp[n][0]dp[n][0]
  • 状态转移:
    dp[i][j]=dp[i−1][(j+a[i])%k]∣dp[i−1][((j−a[i])%k+k)]dp[i][j] = dp[i-1][(j+a[i])\%k]\quad|\quad dp[i-1][((j-a[i])\%k+k)]dp[i][j]=dp[i−1][(j+a[i])%k]∣dp[i−1][((j−a[i])%k+k)].
  • 初态: dp[0][∗]=0,dp[0][0]=1dp[0][*]=0, dp[0][0]=1dp[0][∗]=0,dp[0][0]=1

三、代码

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 10005;
const int MAXK = 105;int dp[MAXN][MAXK]; // dp[i][j]:前i个数模k是否能够等于j
int a[MAXN];
int main()
{int n, k;cin >> n >> k;for(int i=1; i<=n; i++)cin >> a[i];for(int i=0; i<k; i++)dp[0][i]=0;dp[0][0] = 1;for(int i=1; i<=n; i++)for(int j=0; j<k; j++)dp[i][j] = dp[i-1][(j+a[i])%k] | dp[i-1][((j-a[i])%k+k)%k];if(dp[n][0])cout << "YES" << endl;elsecout << "NO" << endl;return 0;
}

最大上升子序列

一、题目大意

二、解题思路

  • 定义dp[i]dp[i]dp[i]:以a[i]a[i]a[i]为结尾的最大子序列和。
  • 目标态:max{dp[k]∣1&lt;=k&lt;=n}max\{dp[k]\quad | \quad 1&lt;=k&lt;=n\}max{dp[k]∣1<=k<=n}
  • 状态转移方程:dp[i]=max(a[i],{dp[j]+a[i]∣j&lt;i&amp;a[j]&lt;a[i]})dp[i] = max(a[i], \{dp[j]+a[i]\quad | \quad j&lt;i \&amp;a[j]&lt;a[i]\})dp[i]=max(a[i],{dp[j]+a[i]∣j<i&a[j]<a[i]})
  • 初态: dp[1]=a[1]dp[1] = a[1]dp[1]=a[1]

三、代码

#include<iostream>
using namespace std;
const int MAXN = 1005;int dp[MAXN];
int a[MAXN];
int main()
{int n;cin >> n;for(int i=1; i<=n; i++)cin >> a[i];dp[1] = a[1];for(int i=2; i<=n; i++){dp[i] = a[i];for(int j=1; j<i; j++){if(a[j] < a[i])dp[i] = max(dp[j]+a[i], dp[i]);}}int ans = -1;for(int i=1; i<=n; i++)ans = max(ans, dp[i]);cout << ans << endl;return 0;
}

怪盗基德的滑翔伞

一、题目大意
直接见题目
二、解题思路
(1)开始时必在一栋建筑物上。
(2)可以选择往左往右。
因此是求往左和往右的两个最长上升子序列。
三、代码

#include<iostream>
using namespace std;
const int MAXN = 105;
int dp1[MAXN];
int dp2[MAXN];
int a[MAXN];
int main()
{int T;cin >> T;while(T--){int n;cin >> n;for(int i=1; i<=n; i++)cin >> a[i];dp1[1] = 1;for(int i=2; i<=n; i++){dp1[i] = 1;for(int j=1; j<i; j++){if(a[j] < a[i])dp1[i] = max(dp1[i], dp1[j]+1);}}dp2[n]=1;for(int i=n-1; i>=1; i--){dp2[i] = 1;for(int j=n; j>i; j--){if(a[i] > a[j])dp2[i] = max(dp2[j]+1, dp2[i]);}}int ans = 0;for(int i=1; i<=n; i++)ans = max(ans, max(dp1[i], dp2[i]));cout << ans << endl;}return 0;
}

宠物小精灵之收服(二维背包)

一、题目大意
这里
二、解题思路
这是一个二维背包问题。

  • 定义dp[i][j][k]dp[i][j][k]dp[i][j][k]: 前iii个宠物中,有jjj个球,皮卡丘体力为kkk的情况下最多收服的数量。
  • 终态argmink{dp[N][Q][k]=dp[N][Q][T]}argmin_{k} \quad \{dp[N][Q][k] = dp[N][Q][T]\}argmink​{dp[N][Q][k]=dp[N][Q][T]}
  • 状态转移: dp[i][j][k]={max(dp[i−1][j][k],dp[i−1][j−qiu[i]][k−t[i]]+1)j&gt;=qiu[i],k&gt;=ti[i]dp[i−1][j][k]dp[i][j][k] = \begin{cases} max(dp[i-1][j][k], dp[i-1][j-qiu[i]][k-t[i]] +1)&amp; j&gt;=qiu[i], k&gt;=ti[i]\\ dp[i-1][j][k] \end{cases}dp[i][j][k]={max(dp[i−1][j][k],dp[i−1][j−qiu[i]][k−t[i]]+1)dp[i−1][j][k]​j>=qiu[i],k>=ti[i]
  • 初始态: dp[0][∗][∗]=0dp[0][*][*]=0dp[0][∗][∗]=0
  • 实现的时候需要压缩维度,不然超时,另外有点小问题,题干中提到体力减为0不能收服,但是在实际解题中却不能考虑该条件。

三、代码

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int l,m,n,minn;
int A[105][2];
int f[1005][505];
int main()
{scanf("%d %d %d",&m,&l,&n);for(int i=1;i<=n;i++){scanf("%d %d",&A[i][0],&A[i][1]);}for(int i=1;i<=n;i++){for(int j=m;j>=A[i][0];j--){for(int k=l;k>=A[i][1];k--){f[j][k]=max(f[j][k],f[j-A[i][0]][k-A[i][1]]+1);}}}printf("%d ",f[m][l]);for(int i=0;i<=l;i++){if(f[m][i]==f[m][l]){minn=i;break;}}printf("%d",l-minn);
}

采方格

一、题目大意

二、解题思路
(1) 搜索法: 由于题中说明了只要任意一步走法不同,就是两种方法,那么基于此我们就能直接进行深搜。
(2) 动态规划: 待思考,可参看博客

三、代码

#include<iostream>
using namespace std;
int flag[50][50];
int dir[3][2] = {{0,1},{0,-1},{1,0}};
int offset = 20;
int cnt;
void dfs(int x, int y, int t)
{if(t == 0){cnt++;return;}for(int i=0; i<3; i++){int nx = x + dir[i][0];int ny = y + dir[i][1];if(flag[nx][ny]) continue;flag[nx][ny] = 1;dfs(nx, ny, t-1);flag[nx][ny] = 0;}return;}
int main()
{int n;cin >> n;cnt = 0;flag[0][offset+0] = 1;dfs(0, 0+offset, n);cout << cnt << endl;
}

开餐馆

一、题目大意

二、解题思路
(1) 首先肯定会在一个地方开餐馆
(2)如果确定在此开餐馆,就要看两边如何开餐馆利润最大。

  • 定义:

    • dp1[i]:dp1[i]:dp1[i]:在iii处开餐馆,区间[1,i][1,i][1,i]开餐馆的最大利润。
    • dp2[i]:dp2[i]:dp2[i]:在iii处开餐馆,区间[i,n][i,n][i,n]开餐馆的最大利润。
  • 目标状态: max({dp1[i]+dp2[i]−p[i]∣1&lt;=i&lt;=n})max(\{dp1[i]+dp2[i]-p[i]\quad|\quad 1&lt;=i&lt;=n\})max({dp1[i]+dp2[i]−p[i]∣1<=i<=n})
  • 状态转移: dp1[i]=max(p[i],{dp1[j]+p[i]∣m[i]−m[j]&gt;k&amp;i&gt;j}dp1[i] = max(p[i],\{dp1[j]+p[i]\quad | \quad m[i]-m[j]&gt;k \&amp; i&gt;j\}dp1[i]=max(p[i],{dp1[j]+p[i]∣m[i]−m[j]>k&i>j}
    (要么独自开,要么前面开的最邻近的一家为前面满足条件的地点之一)
    dp2[i]=max(p[i],{dp2[j]+p[i]∣m[j]−m[i]&gt;k&amp;j&gt;i}dp2[i]=max(p[i], \{dp2[j]+p[i] \quad | \quad m[j]-m[i]&gt;k \&amp; j&gt;i\}dp2[i]=max(p[i],{dp2[j]+p[i]∣m[j]−m[i]>k&j>i}
  • 初态: dp[1]=p[1],dp2[n]=p[n]dp[1]=p[1], dp2[n]=p[n]dp[1]=p[1],dp2[n]=p[n]

三、代码

#include<iostream>
using namespace std;
const int MAXN = 1005;int p[MAXN];
int m[MAXN];
int dp1[MAXN];
int dp2[MAXN];int main()
{int T;cin >> T;while(T--){int n , k;cin >> n >> k;for(int i=1; i<=n; i++)cin >> m[i];for(int i=1; i<=n; i++)cin >> p[i];dp1[1] = p[1];for(int i=2; i<=n; i++){dp1[i] = p[i];for(int j=1; j<i; j++){if(m[i]-m[j] > k){dp1[i] = max(dp1[i], dp1[j]+p[i]);}}}dp2[n] = p[n];for(int i=n-1; i>=1; i--){dp2[i] = p[i];for(int j=n; j>i; j--){if(m[j]-m[i] > k){dp2[i] = max(dp2[i], dp2[j]+p[i]);}}}int ans = 0;for(int i=1; i<=n; i++)ans = max(ans, dp1[i]+dp2[i]-p[i]);cout << ans << endl;}return 0;
}

买书

一、题目大意

二、解题思路

  • 定义dp[i][j]:dp[i][j]:dp[i][j]:前iii件物品刚好花完jjj元钱的总的购买方式总数。
  • 目标状态dp[4][n]dp[4][n]dp[4][n]。
  • 状态转移:
    dp[i][j]=∑0j/a[i]dp[i−1][j−a[i]]dp[i][j] = \sum_0^{j/a[i]}dp[i-1][j-a[i]]dp[i][j]=0∑j/a[i]​dp[i−1][j−a[i]]
    这是完全背包.
  • 初态dp[0][∗]=0,dp[0][0]=1dp[0][*]=0, dp[0][0]=1dp[0][∗]=0,dp[0][0]=1

三、代码

#include<iostream>
using namespace std;int a[5] = {0, 10, 20, 50, 100};
int dp[1000+5];int main()
{int n;cin >> n;for(int i=0; i<=n; i++)dp[i] = 0;dp[0]=1;for(int i=1; i<=4; i++)**加粗样式**for(int j=a[i]; j<=n; j++)dp[j] += dp[j-a[i]];if(n==0)cout << 0 << endl;elsecout << dp[n] << endl;
}

带通配符的字符串匹配

一、题目大意

二、解题思路

  • 定义dp[i][j]∈{0,1}:dp[i][j] \in \{0,1\}:dp[i][j]∈{0,1}:字符串s1s1s1的前iii位和字符串s2s2s2的前jjj位是否能匹配。
  • 目标状态: dp[l1][l2]dp[l1][l2]dp[l1][l2],其中l1、l2l1、l2l1、l2分别位两个字符串长度
  • 状态转移:
    • 选择:

      • (1) 若当前s1[i]=s2[j]∣∣s1[i]=′?′s1[i] = s2[j] || s1[i]='?'s1[i]=s2[j]∣∣s1[i]=′?′, 则s1[i]s1[i]s1[i]和s2[j]s2[j]s2[j]需要匹配。
      • (2) 若当前s1[i]!=s2[j]&amp;&amp;s1[i]!=′∗′s1[i] != s2[j] \&amp;\&amp;s1[i] != '*'s1[i]!=s2[j]&&s1[i]!=′∗′, 则没办法匹配
      • (2) 若当前s1[i]!=s2[j]&amp;&amp;s1[i]==′∗′s1[i]!=s2[j] \&amp;\&amp; s1[i] == '*'s1[i]!=s2[j]&&s1[i]==′∗′, 则可以用星号匹配[0,j][0,j][0,j]个字符,只要其中一个能够匹配就能匹配。
    • 方程式:
      dp[i][j]={dp[i−1][j−1]s1[i]=s2[j]∣∣s1[i]=′?′0s1[i]!=s2[j]&amp;s1[i]!=′∗′∪{dp[i−1][k]}0&lt;=k&lt;=jdp[i][j] = \begin{cases} dp[i-1][j-1] &amp; s1[i]=s2[j]\quad||\quad s1[i]='?' \\ 0 &amp; s1[i] !=s2[j] \&amp; s1[i] !='*' \\ \cup\{dp[i-1][k]\} &amp; 0&lt;=k &lt;=j \end{cases}dp[i][j]=⎩⎪⎨⎪⎧​dp[i−1][j−1]0∪{dp[i−1][k]}​s1[i]=s2[j]∣∣s1[i]=′?′s1[i]!=s2[j]&s1[i]!=′∗′0<=k<=j​
  • 初态: dp[0][∗]=dp[∗][0]=0dp[0][*] = dp[*][0] = 0dp[0][∗]=dp[∗][0]=0, 但是要考虑第一个字符串开始为∗*∗号的情况。具体见代码

三、代码

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;int dp[24][24];
char s1[24];
char s2[24];
int main()
{scanf("%s%s", s1, s2);int l1 = strlen(s1);int l2 = strlen(s2);for(int i=0; i<=max(l1, l2); i++)dp[0][i] = dp[i][0] = 0;dp[0][0] = 1;int i=0;while(s1[i++] == '*' )dp[i][0] = 1;for(int i=1; i<=l1; i++){for(int j=1; j<=l2; j++){if(s1[i-1]==s2[j-1] || s1[i-1] == '?')dp[i][j] = dp[i-1][j-1];else if(s1[i-1] != s2[j-1] && s1[i-1] == '*'){for(int k=0; k<=j; k++){dp[i][j] |= dp[i-1][j-k];}}elsedp[i][j] = 0;}}if(dp[l1][l2])cout << "matched" << endl;elsecout << "not matched" << endl;return 0;
}

放苹果

一、题目大意

二、解题思路
M的N划分数

三、代码

#include<iostream>
using namespace std;const int MAX = 25;
typedef long long ll;
ll dp[MAX][MAX];
int main()
{int t;cin >> t;while(t--){int m, n;cin >> m >> n;for(int i=0; i<MAX; i++)dp[0][i] = 0;dp[0][0] = 1;for(int i=1; i<=n; i++){for(int j=0; j<=m; j++){if(j >= i)dp[i][j] = dp[i-1][j] + dp[i][j-i];elsedp[i][j] = dp[i-1][j];}}cout << dp[n][m] << endl;}return 0;
}

最低通行费

一、题目大意

二、解题思路

三、代码

#include<iostream>
#include<cstring>
using namespace std;const int MAXN = 105;
const int inf = 1 << 30;
int grad[MAXN][MAXN];
int dp[MAXN][MAXN];
int N;
int dfs(int x, int y)
{if(x > N || y > N)return inf;if(dp[x][y] >= 0)return dp[x][y];dp[x][y] = grad[x][y]+min(dfs(x+1,y), dfs(x, y+1));return dp[x][y];
}
int main()
{cin >> N;for(int i=1; i<=N; i++)for(int j=1; j<=N; j++)cin >> grad[i][j];memset(dp,-1,sizeof(dp));dp[N][N] = grad[N][N];cout << dfs(1, 1) << endl;
}

三角形的最佳路径

一、题目大意

三、代码

#include<iostream>
using namespace std;int dp[105][105];
int grad[105][105];
int main()
{int N;cin >> N;for(int i=1; i<=N; i++)for(int j=1; j<=i; j++)cin >> grad[i][j];for(int j=1; j<=N; j++)dp[N][j] = grad[N][j];for(int i=N-1; i>=1; i--){for(int j=1; j<=i; j++){dp[i][j] = max(dp[i+1][j], dp[i+1][j+1])+grad[i][j];}}cout << dp[1][1] << endl;return 0;
}

鸡蛋的硬度

一、题目大意

二、解题思路

  • 定义dp[i][j]:dp[i][j]:dp[i][j]:有iii层楼jjj个鸡蛋最坏的次数。
  • 目标状态dp[n][m]dp[n][m]dp[n][m]
  • 状态转移:
    • 选择:

      • 前iii层可以选择在任意一层扔。
      • 在第k层扔可能碎或者没碎。
      • 最优策略即选择最优的层iii,最坏情况即碎或者没碎中最坏
    • 状态转移方程式:
      dp[i][j]=min({max(dp[k−1][j−1],dp[i−k][j])+1∣1&lt;=k&lt;=i}dp[i][j] = min(\{max(dp[k-1][j-1],dp[i-k][j])+1\quad | \quad 1&lt;=k&lt;=i\}dp[i][j]=min({max(dp[k−1][j−1],dp[i−k][j])+1∣1<=k<=i}
  • 初始状态: dp[i][1]=i,dp[0][j]=0dp[i][1] = i, dp[0][j]=0dp[i][1]=i,dp[0][j]=0(更新过程不会用到j=0j=0j=0的状态, 因此不必去定义)

三、代码

#include<iostream>
#include<cstring>
using namespace std;int dp[105][12];
const int inf = 1<<30;
int main()
{int n, m;while(cin >> n >> m){for(int i=1; i<=n; i++)dp[i][1] = i;for(int j=0; j<=m; j++)dp[0][j] = 0;for(int i=1; i<=n; i++){for(int j=2; j<=m; j++){dp[i][j] = inf;for(int k=1; k<=i; k++){dp[i][j] = min(dp[i][j], max(dp[k-1][j-1],dp[i-k][j])+1);}}}cout << dp[n][m] << endl;}return 0;
}

大盗阿福

一、题目大意

二、解题思路

  • 定义dp[i]dp[i]dp[i]:前iii个商店抢劫的最大价值。
  • 目标状态dp[n]dp[n]dp[n].
  • 状态转移:
    • 选择: 抢该商店或者不抢
    • 方程式:dp[i]=max(dp[i−1],dp[i−2]+a[i])dp[i] = max(dp[i-1], dp[i-2]+a[i])dp[i]=max(dp[i−1],dp[i−2]+a[i])
  • 初始状态: dp[0]=0,dp[1]=a[i]dp[0] = 0, dp[1] = a[i]dp[0]=0,dp[1]=a[i]

三、代码

#include<iostream>
#include<stdio.h>
using namespace std;const int MAXM = 100005;
int dp[MAXM], a[MAXM];
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]);dp[1] = a[1];dp[0] = 0;for(int i=2; i<=n; i++)dp[i] = max(dp[i-1], dp[i-2]+a[i]);printf("%d\n", dp[n]);}return 0;
}

切割回文

一、题目大意

二、解题思路

  • 定义:

    • dp[i]:dp[i]:dp[i]:[0,i−1][0,i-1][0,i−1]组成的字串需要切成回文需要的最少次数。
    • m[i][j]:m[i][j]:m[i][j]: [i,j][i,j][i,j]组成的字串是否是回文。
  • 目标状态dp[L−1]dp[L-1]dp[L−1].
  • 状态转移:
    • 选择: 如果[0,i][0,i][0,i]是回文,则dp[i]=0dp[i]=0dp[i]=0, 如果不是,那么考虑从右到左,第一刀切在哪里.
    • 状态转移方程: dp[i]={0m[0][i]=1min({dp[j]+1∣m[j+1][i]=1,0&lt;=j&lt;=i−1})elsedp[i] = \begin{cases} 0 &amp; m[0][i]=1 \\ min(\{dp[j]+1\quad | \quad m[j+1][i]=1,0&lt;=j&lt;=i-1\}) &amp; else \end{cases}dp[i]={0min({dp[j]+1∣m[j+1][i]=1,0<=j<=i−1})​m[0][i]=1else​
  • 初态:
    • m[i][i]=1,m[i][i+1]=(s[i]==s[i+1])m[i][i]=1, m[i][i+1] = (s[i]==s[i+1])m[i][i]=1,m[i][i+1]=(s[i]==s[i+1])
    • dp[0]=0dp[0]=0dp[0]=0

三、代码

#include<iostream>
#include<cstring>
using namespace std;
const int MAX = 1005;
int is_hui[MAX][MAX];
int dp[MAX];
int main()
{int T;cin >> T;while(T--){string s;cin >> s;int L = s.length();memset(is_hui, 0, sizeof(is_hui));for(int i=0; i<L; i++)is_hui[i][i] = 1;for(int i=0; i<L-1; i++)if(s[i] == s[i+1])is_hui[i][i+1] = 1;for(int l=3; l<=L; l++){for(int i=0; i+l-1<=L-1; i++){int j = i+l-1;if(s[i] == s[j]){if(is_hui[i+1][j-1])is_hui[i][j] = 1;elseis_hui[i][j] = 0;}elseis_hui[i][j] = 0;}}dp[0] = 0;for(int i=1; i<L; i++){if(is_hui[0][i])dp[i] = 0;else{dp[i] = 100000;for(int j=0; j<=i-1; j++){if(is_hui[j+1][i])dp[i] = min(dp[i], dp[j]+1);}}}cout << dp[L-1] << endl;}
}

乘积最大

一、题目大意

二、解题思想

  • 定义dp[i][j]:dp[i][j]:dp[i][j]:前iii个数字方jjj个符号的最大值
  • 终态:dp[n][k]dp[n][k]dp[n][k]
  • 状态转移:
    • 选择: 将最后一个符号放在第k个数字之后
    • 状态转移方程式: dp[i][j]=max({dp[k][j−1]∗get_num(k+1,i)∣j&lt;=k&lt;=i−1})dp[i][j] = max(\{dp[k][j-1]*get\_num(k+1,i) \quad | \quad j&lt;=k&lt;=i-1\})dp[i][j]=max({dp[k][j−1]∗get_num(k+1,i)∣j<=k<=i−1})
      其中get_num(i,j)get\_num(i,j)get_num(i,j)用来求第iii位到第jjj位组成的数字.k&gt;=jk&gt;=jk>=j的原因是需要在前面放j−1j-1j−1个符号,因此至少需要jjj个数字。
  • 初态: dp[i][0]=get_num(1,i)dp[i][0]=get\_num(1,i)dp[i][0]=get_num(1,i)

三、代码

#include<iostream>
#include<stdio.h>
using namespace std;int dp[45][7];
int a[45];
int get_num(int s, int e)
{int res = 0;for(int i=s; i<=e; i++)res = res*10 + a[i];return res;
}
int main()
{int n, k;cin >> n >> k;for(int i=1; i<=n; i++)scanf("%1d", &a[i]);for(int i=1; i<=n; i++)dp[i][0] = get_num(1,i);for(int i=2; i<=n; i++){for(int j=1; j<i; j++){dp[i][j]=0;for(int k=j; k<=i-1; k++){dp[i][j] = max(dp[i][j], dp[k][j-1]*get_num(k+1,i));}}}cout << dp[n][k] << endl;return 0;
}

装箱问题

一、题目大意

二、解题思路
0-1背包判断是否可凑齐

三、代码

#include<iostream>
#include<cstring>
using namespace std;const int MAXV = 20005;
int dp[MAXV];
int a[33];
int main()
{int V;cin >> V;int n;cin >> n;for(int i=1; i<=n; i++)cin >> a[i];memset(dp, 0, sizeof(dp));dp[0] = 1;for(int i=1; i<=n; i++)for(int j=V; j>=a[i]; j--)dp[j] = dp[j] | dp[j-a[i]];int ans = MAXV;for(int i=V; i>=0; i--){if(dp[i])ans = min(ans, V-i);}cout << ans << endl;return 0;
}

方格取数

一、题目大意

二、解题思路
(WA):不可以认为先取最大,再去次大。
(AC): 同时模拟两个人行走。

  • 定义dp[i][j][k][h]:dp[i][j][k][h]:dp[i][j][k][h]:第一个人走到(i,j)(i,j)(i,j),第二个人走到(k,h)(k,h)(k,h)最优解。
  • 目标dp[n][n][n][n]dp[n][n][n][n]dp[n][n][n][n]
  • 状态转移:
    • 选择: (i,j,k,h)(i,j,k,h)(i,j,k,h)可以由(i−1,j,k−1,h)、(i−1,j,k,h−1),(i,j−1,k−1,h)、(i,j−1,k,h−1)(i-1,j,k-1,h)、(i-1,j,k,h-1),(i, j-1,k-1,h)、(i,j-1,k,h-1)(i−1,j,k−1,h)、(i−1,j,k,h−1),(i,j−1,k−1,h)、(i,j−1,k,h−1)转移过来.
    • 状态转移方程式dp[i][j][k][h]={max(4个状态的dp值)+grad[i][j]+grad[k][h](i,j)=(k,h)max(4个状态的dp值)+grad[i][j]elsedp[i][j][k][h]=\begin{cases} max(4个状态的dp值)+grad[i][j]+grad[k][h] &amp; (i,j)=(k,h) \\ max(4个状态的dp值)+grad[i][j] &amp; else \end{cases}dp[i][j][k][h]={max(4个状态的dp值)+grad[i][j]+grad[k][h]max(4个状态的dp值)+grad[i][j]​(i,j)=(k,h)else​
  • 初态:全部置为0即可

三、代码

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;int n,i,j,tmp,k,l;
int puz[20][20], dp[20][20][20][20];
int main()
{scanf("%d",&n);while(scanf("%d%d%d", &i, &j, &tmp) && i)puz[i][j] = tmp;memset(dp, 0, sizeof(dp));for(int i=1; i<=n; i++){for(int j=1; j<=n; j++){for(int k=1; k<=n; k++){for(int h=1; h<=n; h++){int tmp1 = max(dp[i-1][j][k-1][h], dp[i-1][j][k][h-1]);int tmp2 = max(dp[i][j-1][k-1][h], dp[i][j-1][k][h-1]);if(i==k && j == h)dp[i][j][k][h] = max(tmp1, tmp2) + puz[i][j];elsedp[i][j][k][h] = max(tmp1, tmp2) + puz[i][j] + puz[k][h];}}}}cout << dp[n][n][n][n] << endl;return 0;
}

滑雪

一、题目大意

二、解题思路

  • 定义dp[i][j]:dp[i][j]:dp[i][j]:为以方格(i,j)(i,j)(i,j)为截止的路线的最长长度。
  • 然后脑海中模拟三维立体图像,模拟记忆化搜索过程。
    三、代码
#include<iostream>
#include<cstring>
using namespace std;const int MAX = 105;
int dp[MAX][MAX];
int grad[MAX][MAX];
int c, r;
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
int dfs(int x, int y)
{if(dp[x][y] > 0)return dp[x][y];dp[x][y] = 1;for(int i=0; i<4; i++){int nx = x + dir[i][0];int ny = y + dir[i][1];if(x < 1 || x > c || y < 1 || y > r)continue;if(grad[nx][ny] > grad[x][y])dp[x][y] = max(dp[x][y], dfs(nx, ny)+1);}return dp[x][y];
}
int main()
{cin >> c >> r;for(int i=1; i<=c; i++)for(int j=1; j<=r; j++)cin >> grad[i][j];memset(dp, -1, sizeof(dp));int ans = 0;for(int i=1; i<=c; i++)for(int j=1; j<=r; j++)ans = max(ans, dfs(i,j));cout << ans << endl;
}

核电站问题

题解

酒鬼

一、题目大意

二、解题思路

  • 定义dp[i]:dp[i]:dp[i]:前iii壶酒能喝的最大体积
  • 目标状态: dp[n]dp[n]dp[n]
  • 状态转移:
    • 选择: (1)是否喝第iii壶酒(2)若喝,是否喝第i−1i-1i−1壶
    • 状态转移方程: dp[i]=max(dp[i−1],max(dp[i−2]+a[i],dp[i−3]+a[i−1]+a[i]))dp[i] = max(dp[i-1], max(dp[i-2]+a[i], dp[i-3]+a[i-1]+a[i]))dp[i]=max(dp[i−1],max(dp[i−2]+a[i],dp[i−3]+a[i−1]+a[i]))
  • 初始状态: dp[0]=0,dp[1]=a[1],dp[2]=a[2]dp[0]=0, dp[1]=a[1], dp[2]=a[2]dp[0]=0,dp[1]=a[1],dp[2]=a[2]

三、代码

#include<iostream>
using namespace std;const int maxn=705;
int dp[maxn];
int a[maxn];
int main()
{int n;cin >> n;for(int i=1; i<=n; i++)cin >> a[i];dp[0]=0;dp[1] = a[1];dp[2] = a[1] + a[2];for(int i=3; i<=n; i++)dp[i] = max(max(dp[i-3]+a[i-1]+a[i],dp[i-2]+a[i]), dp[i-1]);cout << dp[n] << endl;return 0;
}

Pku2440 DNA

一、题目大意
长度为LLL的0-1串,不包含101101101和111111111的子串有多少种
二、解题思路

  • 定义:dp[i]dp[i]dp[i]:长度为iii的串包含合法字串的种数。
  • 目标: dp[n]dp[n]dp[n]
  • 状态转移:
    • 选择: (1)第iii位是1或0。(2)第i−1i-1i−1位是1或0
    • 状态转移方程: dp[i]=dp[i−1]+dp[i−3]+dp[i−4]dp[i] = dp[i-1]+dp[i-3]+dp[i-4]dp[i]=dp[i−1]+dp[i−3]+dp[i−4]
  • 初态.dp[0]=1,dp[1]=2,dp[2]=4,dp[3]=6dp[0]=1, dp[1]=2,dp[2]=4, dp[3]=6dp[0]=1,dp[1]=2,dp[2]=4,dp[3]=6
    三、代码
#include<iostream>
using namespace std;
const int mod = 2005;
int dp[1000000+6];
int main()
{int n;cin >> n;dp[0]=1;dp[1]=2;dp[2]=4;dp[3]=6;for(int i=4; i<=n; i++)dp[i] = (dp[i-1]+dp[i-3]+dp[i-4])%mod;cout << dp[n] << endl;return 0;
}

奶牛散步

一、题目大意

二、解题思路

  • 定义dp[i]:dp[i]:dp[i]:走iii步的路线数。
  • 目标状态dp[n]dp[n]dp[n]
  • 状态转移:
    • 来源: 走iii步来源于走i−1i-1i−1步后再走一步。这一步可以(1)向上走(2)向左走(3)向右走。
    • 若i−1i-1i−1步的每一个状态都能往三个方向走,则有dp[i]=3∗dp[i−1]dp[i]=3*dp[i-1]dp[i]=3∗dp[i−1],但是很明显有一些只能向上向右或者向上向左走的状态,这样的状态共(dp[i−1]−dp[i−2]dp[i-1]-dp[i-2]dp[i−1]−dp[i−2])个其中dp[i−2]dp[i-2]dp[i−2]是指i−1i-1i−1步状态中三个方向都能走的状态(因为它是由i−2i-2i−2步中左右状态向上走转移过来的)。
    • 状态转移方程: dp[i]=2∗dp[i−1]+dp[i−2]dp[i]=2*dp[i-1]+dp[i-2]dp[i]=2∗dp[i−1]+dp[i−2]
  • 初始状态: dp[0]=1,dp[1]=3dp[0]=1, dp[1]=3dp[0]=1,dp[1]=3

三、代码

#include<iostream>
using namespace std;int main()
{int dp[1004];int N;dp[0]=1;dp[1]=3;dp[2]=7;int n;cin >> n;for(int i=3; i<=n; i++)dp[i] = (2*dp[i-1]+dp[i-2])%12345;cout << dp[n] << endl;
}

[Usaco2009 Feb]Bullcow

一、题目大意

二、解题思路

  • 定义dp[i]:dp[i]:dp[i]:iii头牛再条件kkk的情况下的安排方式。
  • 目标状态dp[n]dp[n]dp[n]
  • 状态转移:
    • 选择: 第iii头牛放奶牛还是放公牛。

      • 放奶牛: 有dp[i−1]dp[i-1]dp[i−1]
      • 放公牛: 有dp[i−m−1]dp[i-m-1]dp[i−m−1]
    • 状态转移方程: dp[i]=dp[i−1]+dp[i−m−1]dp[i] = dp[i-1]+dp[i-m-1]dp[i]=dp[i−1]+dp[i−m−1]
  • 初态: dp[0]=1,dp[i]=1+i(i&lt;=m)dp[0]=1, dp[i]=1+i(i&lt;=m)dp[0]=1,dp[i]=1+i(i<=m)

三、代码

#include<iostream>
using namespace std;
const int mod = 5000011;int dp[100000+5];
int n,m;
int main()
{cin >> n >> m;dp[0]=1;for(int i=1; i<=m; i++)dp[i] = 1 + i;for(int i=m+1; i<=n; i++)dp[i] = (dp[i-1]+dp[i-m-1])%mod;cout << dp[n] << endl;
}

Logs Stacking堆木头

一、题目大意

底层nnn个木头,有多少种堆积方法。

二、解题思路

  • 定义状态dp[i]:dp[i]:dp[i]:底层iii个木头的堆积方法。
  • 目标状态dp[n]dp[n]dp[n]
  • 很容易得出递推式: dp[i]=dp[i−1]+2∗dp[i−2]+3∗dp[i−3]+...+(n−1)∗dp[1]+1dp[i] = dp[i-1]+2*dp[i-2]+3*dp[i-3]+...+(n-1)*dp[1]+1dp[i]=dp[i−1]+2∗dp[i−2]+3∗dp[i−3]+...+(n−1)∗dp[1]+1
    • 化简:dp[i−1]=dp[i−2]+2∗dp[i−3]+...+(n−2)∗dp[1]+1dp[i-1]=dp[i-2]+2*dp[i-3]+...+(n-2)*dp[1]+1dp[i−1]=dp[i−2]+2∗dp[i−3]+...+(n−2)∗dp[1]+1
    • 令sum[i]=dp[i]+dp[i−1]+...+dp[1]sum[i] = dp[i]+dp[i-1]+...+dp[1]sum[i]=dp[i]+dp[i−1]+...+dp[1]
    • 则dp[i]=dp[i−1]+sum[i−1]dp[i]=dp[i-1]+sum[i-1]dp[i]=dp[i−1]+sum[i−1]
  • 初态: dp[0]=1,sum[0]=0dp[0]=1, sum[0]=0dp[0]=1,sum[0]=0

ACM: 百练NOI——基本算法之动态规划相关推荐

  1. 百练 openjudge 开餐馆(动态规划)

    4118:开餐馆 总时间限制: 1000ms  内存限制:  65536kB 描述 北大信息学院的同学小明毕业之后打算创业开餐馆.现在共有n 个地点可供选择.小明打算从中选择合适的位置开设一些餐馆.这 ...

  2. 百练noi 20:反反复复

    20:反反复复 查看 提交 统计 提问 总时间限制:  1000ms  内存限制:  65536kB 描述 Mo和Larry发明了一种信息加密方法.他们首先决定好列数,然后将信息(只包含字母)从上往下 ...

  3. 百练noi 22:神奇的幻方

    22:神奇的幻方 查看 提交 统计 提问 总时间限制:  1000ms  内存限制:  65535kB 描述 幻方是一个很神奇的N*N矩阵,它的每行.每列与对角线,加起来的数字和都是相同的. 我们可以 ...

  4. 北大培训课动态规划----神奇的口袋(百练2755)

    北京大学暑期课<ACM/ICPC竞赛训练> ppt摘取 什么是动态规划? ●递归到动规的一般转化方法  递归函数有n个参数,就定义一个n维的数组,数组 的下标是递归函数参数的取值范围,数组 ...

  5. 58 - 算法 - 百练 2503:Babelfish 二分查找与存储

    #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cmath ...

  6. NOI入门级:算法之动态规划

    糖糖讲动态规划算法,找零钱完全背包问题,LeetCode 322 糖糖讲动态规划算法,找零钱完全背包问题,LeetCode 322_哔哩哔哩_bilibili 程序员面试再也不怕动态规划了,看动画,学 ...

  7. 58 - 算法 -分治问题 - 循环 二分查找 OpenJudge 百练 4143和为给定数

    #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cmath ...

  8. 程序设计入门经典题解(百练篇)

    参考链接:PKU百练题解(Bailian) Bailian1017 装箱问题[贪心] - 海岛Blog - CSDN博客 POJ1088 Bailian1088 滑雪[DFS+记忆化搜索]_海岛Blo ...

  9. 百练 求排列的逆序数

    百练 求排列的逆序数 总时间限制: 内存限制: 1000ms 65536kB 描述 在Internet上的搜索引擎经常需要对信息进行比较,比如可以通过某个人对一些事物的排名来估计他(或她)对各种不同信 ...

最新文章

  1. Python系统的下载与安装教程
  2. 第十三课.随机近似初步:蒙特卡洛方法
  3. access在哪里可以设主键_access利用DAO设置数据表的主键
  4. js获取URL请求参数与改变src
  5. 前端开发学习笔记(二)
  6. CodeFirst实战:用文本数据库存档软件配置
  7. JavaScript实现z-algorithm算法(附完整源码)
  8. C#宿舍管理系统数据表文档分析含释义
  9. junit - no runnable methods
  10. B - Labyrinth Gym - 102798B
  11. bitmap 转 drawable
  12. linux 显示文件名写到txt,C++获取某个路径下所有文件的文件名,读写TXT文件到新的文件...
  13. T-SQL备忘(2):聚合函数运算和NULL
  14. 计算机辅助初中英语教学,计算机辅助初中英语阅读教学的-研究.pdf
  15. 大数据学习笔记30:搭建高可用Hadoop集群
  16. 微信团队的深度学习框架deepx_core开源啦
  17. 在 chrome 中使用 coap 调试插件 copper
  18. Python-selenium:鼠标键盘事件
  19. 关于Facebook,Linkedin网的数据采集总结
  20. 远程桌面与本计算机共享文件,win7系统开启远程桌面共享文件的方法

热门文章

  1. 【计算理论】下推自动机 PDA ( 上下文无关语言 CFL 的 泵引理 | 泵引理反证示例 | 自动机扩展 )
  2. 《面向对象程序设计》课程设计
  3. 热点追踪 | 数据,想说爱你不容易
  4. AD域帐户密码过期,终端802.1x认证自动重连导致AD账号被锁,员工无法上网、办公怎么办?
  5. el-select组件设置focus时placeholder的文字提示
  6. 再探HEVC——理解不同类型的I帧
  7. python二手房使用教程_利用Python对链家网北京二手房进行简单数据分析
  8. 让网站加载速度更快的10种方法
  9. 如何将C 项目部署到云服务器上,如何将C 应用程序放在云服务器上
  10. K2 BPM_北汽新能源业务流程管理信息系统建设思考_全球领先的工作流引擎