一些DP题(顺便找的)
注:一日恰饭之余,LL大佬说刷啥线段树不如做些DP题,想想也是==然后刷了几道DP题
https://vjudge.net/contest/399437#overview
后续写了上面contest的题,在这篇随笔继续更新
两道下饭题

思路:数字三角形,最简单的DP,没有之一
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[210][210];
ll a[210][210];
int main (){
int T;
scanf("%d", &T);
int num = 1;
while(T--) {
int N;
scanf("%d", &N);
memset(dp, 0, sizeof dp);
memset(a, 0,sizeof a);
for(int i = 1; i <= 2 * N - 1; ++i) {
if(i <= N){
for(int j = 1; j <= i; ++j) {
scanf("%lld", &a[i][j]);
}
}
else {
for(int j = 1; j <= 2 * N - i; ++j) {
scanf("%lld", &a[i][j]);
}
}
}
dp[1][1] = a[1][1];
for(int i = 2; i <= 2 * N - 1; ++i) {
if(i <= N){
for(int j = 1; j <= i; ++j) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + a[i][j];
}
}
else {
for(int j = 1; j <= 2 * N - i; ++j) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j + 1]) + a[i][j];
}
}
}
// for(int i = 1; i <= 2 * N - 1; ++i) {
// if(i <= N){
// for(int j = 1; j <= i; ++j) {
// printf("%lld ", dp[i][j]);
// }
// }
// else {
// for(int j = 1; j <= 2 * N - i; ++j) {
// printf("%lld ", dp[i][j]);
// }
// }
// putchar(10);
// }
printf("Case %d: %lld\n", num++, dp[2 * N - 1][1]);
}
}
/*
2
4
7
6 4
2 5 10
9 8 12 2
2 12 7
8 2
10
2
1
2 3
1
*/

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[25][10];
ll a[25][10];
int main () {
int n;
scanf("%d", &n);
int num = 1;
while(n--) {
getchar();
int t;
scanf("%d", &t);
for(int i = 1; i <= t; ++i) {
for(int j = 1; j <= 3; ++j) {
scanf("%lld", &a[i][j]);
if(i == 1) {
dp[i][j] = a[i][j];
}
}
}
for(int i = 2; i <= t; ++i) {
for(int j = 1; j <= 3; ++j) {
ll minn = 0x3f3f3f3f;
for(int k = 1; k <= 3; ++k) {
if(j != k)
minn = min(dp[i - 1][k], minn);
}
dp[i][j] = minn + a[i][j];
}
}
ll ans = 0x3f3f3f3f;
for(int i = 1; i <= 3; i++) {
ans = min(ans, dp[t][i]);
}
printf("Case %d: %lld\n", num++, ans);
}
}
/*
2
4
13 23 12
77 36 64
44 89 76
31 78 45
3
26 40 83
49 60 57
13 89 99
*/
思路:与上题一样
今日做了这两道题发现这个contest属实easy
然后上网搜了波
被虐自闭

先使用前缀和预处理,然后可以知道状态转移方程dp[i][j] = max(dp[i -1][j], dp[i - m][j - 1] + sum[i] - sum[i - m]);前者代表不选它,后者代表选它情况的和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[5010][5010];
ll sum[5010];
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
ll n, m, k;
cin >> n >> m >> k;
memset(sum, 0, sizeof sum);
memset(dp, 0, sizeof dp);
for(int i = 1; i <= n; ++i) {
ll temp;
cin >> temp;
sum[i] = sum[i - 1] + temp;
}
//代表前i个元素,选j组的情况的和
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= k; ++j) {
if(i - m >= 0)
dp[i][j] = max(dp[i -1][j], dp[i - m][j - 1] + sum[i] - sum[i - m]);
}
}
cout << dp[n][k] << endl;
}

数论与DP的集合,重点关注右边开区间的个数,如果查一个是可以通过自身自加来满足,如果刚好合适的话就只用关注前i - 1(dp[i][j] = (dp[i][j] + dp[i - 1][j]) % MODE;)反之(dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % MODE;)下面同理
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MODE = 1e9 + 7;
ll dp[2020][2020];//dp[i][j]代表前i个元素中, 右边有j个开区间的情况
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
ll n, h;
cin >> n >> h;
vector<ll> a(n + 1);
for(int i = 1; i <= n; ++i) {
cin >>a[i];
}
memset(dp, 0, sizeof dp);
dp[1][0] = (a[1] == h || a[1] + 1 == h) ? 1 : 0;
dp[1][1] = a[1] + 1 == h ? 1 : 0;
for(ll i = 2; i <= n; ++i) {
for(ll j = max(h - a[i] - 1, 0ll); j <= min(i, h - a[i]); ++j){
// cout<<i<<' '<<j<<' '<<a[i]<<endl;
if(a[i] + j == h) {
dp[i][j] = (dp[i][j] + dp[i - 1][j]) % MODE;
if(j != 0) {
dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % MODE;
}
}
if(a[i] + j + 1 == h) {
dp[i][j] = (dp[i][j] + dp[i - 1][j] * (j + 1)) % MODE;
dp[i][j] = (dp[i][j] + dp[i - 1][j + 1] * (j + 1)) % MODE;
}
}
}
cout << dp[n][0] << endl;
}
此题属实烧脑,目测银牌题水平
更新于 2020-10-06 21:25:26
作者:LightAc
出处:https://www.cnblogs.com/lightac/
联系:
Email: dzz@stu.ouc.edu.cn
QQ: 1171613053
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号