一些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
标签:int,ll,memset,long,顺便,DP,一些,sum,dp 来源: https://www.cnblogs.com/lightac/p/13775576.html