寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄|

TLE_Automation

园龄:2年9个月粉丝:19关注:23

dp 百题计划 (11 / 100)

P3205 [HNOI2010]合唱队

区间 dp,考虑 dp[l][r][0/1] 表示让 [l,r] 这一区间符合并且最后是从左 / 右 进来的。

转移的话,考虑 [l,r] 这段区间,可能是 [l,r1][l+1,r] 这两段区间转移来的。

而这俩段区间都可能是从左边进来的或者从右边进来的,符合条件就转移即可。

int n, a[N], dp[1020][1020][2];
signed main()
{
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++) dp[i][i][0] = 1;
for(int len = 2; len <= n; len++) {
for(int l = 1; l <= n - len + 1; l++) {
int r = l + len - 1;
if(a[l] < a[l + 1]) dp[l][r][0] = (dp[l][r][0] + dp[l + 1][r][0]) % mod;
if(a[l] < a[r]) dp[l][r][0] = (dp[l][r][0] + dp[l + 1][r][1]) % mod;
if(a[r] > a[r - 1]) dp[l][r][1] = (dp[l][r][1] + dp[l][r - 1][1]) % mod;
if(a[r] > a[l]) dp[l][r][1] = (dp[l][r][1] + dp[l][r - 1][0]) % mod;
}
}
printf("%d\n", (dp[1][n][1] + dp[1][n][0]) % mod);
return 0;
}

P4170 [CQOI2007]涂色

区间dp, 考虑 dp[l][r] 表示将 [l,r] 涂完的最小次数。

如果说 l=r,答案明显是 1

如果说 s[l]==s[r](lr) 可以少涂一次 l 或者少涂一次 r

所以这部分的转移方程是 :

dp[l][r]=min(dp[l][r1],dp[l+1][r])

如果说 s[l]s[r] ,枚举端点。

dp[l,r]=min(dp[l][r],dp[l][k]+dp[k+1][r])

char s[N];
int n, dp[100][100];
signed main()
{
scanf("%s", s + 1);
n = strlen(s + 1);
memset(dp, 0x3f, sizeof dp);
for(int i = 1; i <= n; i++) dp[i][i] = 1;
for(int len = 2; len <= n; len++) {
for(int l = 1, r = l + len - 1; l <= n - len + 1; l++, r++) {
if(s[l] == s[r]) dp[l][r] = min(dp[l][r - 1], dp[l + 1][r]);
else
for(int k = l; k < r; k++) dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]);
}
}
printf("%d\n", dp[1][n]);
return 0;
}

P3842 [TJOI2007]线段

dpi,0/1 表示走到第 i 条线段,0 是在做左端点结束的,1 是在右端点结束的。

分类讨论,从上一行转移,注意要取绝对值。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e4 + 10;
inline int read() {
int s = 0, f = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for(; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ '0');
return f ? -s : s;
}
int n, dp[MAXN][2], l[MAXN], r[MAXN];
int main() {
n = read();
for(int i = 1; i <= n; i++) l[i] = read(), r[i] = read();
dp[1][0] = r[1] + (r[1] - l[1] + 1), dp[1][1] = r[1] - 1;
for(int i = 2; i <= n; i++) {
dp[i][0] = min(dp[i - 1][0] + abs(r[i] - l[i - 1]) + abs(r[i] - l[i] + 1), dp[i - 1][1] + abs(r[i] - r[i - 1]) + abs(r[i] - l[i] + 1));
dp[i][1] = min(dp[i - 1][0] + abs(l[i] - l[i - 1]) + abs(r[i] - l[i] + 1), dp[i - 1][1] + abs(r[i - 1] - l[i]) + abs(r[i] - l[i] + 1));
}
return printf("%d\n", min(dp[n][0] + abs(n - l[n]), dp[n][1] + abs(n - r[n]))), 0;
}

ABC 267 D

一眼 dp,设 dpi,j 为到第 i 个位置,选了 j 个。

所以 dpi,j=max(dpi1,j,dpi1,j1+a[i]×j)

十分显然,但是注意初始化。

int n, a[N], m;
int dp[2020][2020];
signed main() {
n = read(), m = read();
for (int i = 0; i <= n; i++) for (int j = 1; j <= n; j++) dp[i][j] = -inf;
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= min(i, m); j++) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + a[i] * j);
}
}
int Max = -inf;
for (int i = 1; i <= n; i++) Max = max(Max, dp[i][m]);
cout << Max;
return (0 - 0);
}

P1107 [BJWC2008]雷涛的小猫

比较一眼的 dp 吧。

dpi,j 表示到了第 i 层高度,第 j 颗树的最大柿子数量。

dpi,j=max{dpi1,j,maxi=1ndpidel,j}+ai,j

然后可以用一个数组来维护最大值,就做到了 O(n2) 的复杂度。

/**
* author: TLE_Automation
* creater: 2022.10.14
**/
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int Maxn = 2e3 + 10;
const int mod = 998244353;
const ll inf = 0x3f3f3f3f3f3f3f3f;
#define debug cout << "i ak ioi" << "\n"
inline void print(int x) {if (x < 0) putchar('-'), x = -x; if(x > 9) print(x / 10); putchar(x % 10 + '0');}
inline char readchar() {static char buf[100000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;}
inline int read() { int res = 0, f = 0; char ch = gc();for (; !isdigit(ch); ch = gc()) f |= (ch == '-'); for (; isdigit(ch); ch = gc()) res = (res << 1) + (res << 3) + (ch ^ '0'); return f ? -res : res;}
int n, h, del, a[Maxn][Maxn], dp[Maxn][Maxn], Max[Maxn];
/*
dp[i][j] 表示到了第 i 层高度,第 j 颗树的最大柿子数量
*/
signed main()
{
n = read(), h = read(), del = read();
for(int i = 1; i <= n; i++) {
int x = read();
for(int j = 1; j <= x; j++) a[read()][i]++;
}
for(int i = 1; i <= h; i++) {
for(int j = 1; j <= n; j++) {
if(i > del) dp[i][j] = std::max(dp[i - 1][j], Max[i - del]) + a[i][j];
else dp[i][j] = dp[i - 1][j] + a[i][j];
Max[i] = std::max(Max[i], dp[i][j]);
}
}
int ans = 0;
for(int i = 1; i <= n; i++) ans = std::max(ans, dp[h][i]);
cout << ans;
return (0 - 0);
}

P1833 樱花

多重背包板子。

二进制分组优化一下就行了, 然后如果说可以无限选的话,就设 k 为一个较大的数就行了。

/**
* author: TLE_Automation
* creater: 2022.10.14
**/
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int mod = 998244353;
const ll inf = 0x3f3f3f3f3f3f3f3f;
#define debug cout << "i ak ioi" << "\n"
inline void print(int x) {if (x < 0) putchar('-'), x = -x; if(x > 9) print(x / 10); putchar(x % 10 + '0');}
inline char readchar() {static char buf[100000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;}
inline int read() { int res = 0, f = 0; char ch = gc();for (; !isdigit(ch); ch = gc()) f |= (ch == '-'); for (; isdigit(ch); ch = gc()) res = (res << 1) + (res << 3) + (ch ^ '0'); return f ? -res : res;}
int n, V, val[N], tim[N], idx = 0, dp[N];
int h1, h2, m1, m2;
signed main()
{
cin >> h1; gc(); cin >> m1; cin >> h2; gc(); cin >> m2;
if(m2 >= m1) V = (h2 - h1) * 60 + (m2 - m1);
else h2--, m2 += 60, V = (h2 - h1) *60 + (m2 - m1);
n = read();
for(int i = 1; i <= n; i++) {
int vv = read(), ww = read(), k = read();
if(!k) k = 1e6;
for(int j = 1; j <= k; j <<= 1) {
val[++idx] = ww * j, tim[idx] = vv * j;
k -= j;
}
val[++idx] = ww * k, tim[idx] = vv * k;
}
for(int i = 1; i <= idx; i++) {
for(int j = V; j >= tim[i]; j--) {
dp[j] = max(dp[j], dp[j - tim[i]] + val[i]);
}
}
cout << dp[V] << "\n";
return (0 - 0);
}

P1064 [NOIP2006 提高组] 金明的预算方案

有依赖问题的背包dp。

分类讨论。

  • 不买
  • 只卖主件
  • 主件+第一个附件
  • 主件+第二个附件
  • 主件+第一个附件+第二个附件

转移的时候还是 01 背包转移就行了。

/**
* author: TLE_Automation
* creater: 2022.10.14
**/
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mp make_pair
#define pb push_back
#define gc getchar
#define int long long
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int mod = 998244353;
const ll inf = 0x3f3f3f3f3f3f3f3f;
#define debug cout << "i ak ioi" << "\n"
inline void print(int x) {if (x < 0) putchar('-'), x = -x; if(x > 9) print(x / 10); putchar(x % 10 + '0');}
inline char readchar() {static char buf[100000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;}
inline int read() { int res = 0, f = 0; char ch = gc();for (; !isdigit(ch); ch = gc()) f |= (ch == '-'); for (; isdigit(ch); ch = gc()) res = (res << 1) + (res << 3) + (ch ^ '0'); return f ? -res : res;}
int V, n, dp[N];
int cnt[N];
pair<int, int> a[N][5]; // first->tim second->val
/*
1.不买
2.只卖主件
3.主件+1
4.主件+2
5.主件+1,2
*/
#define Val(x,y) a[x][y].second
#define Tim(x,y) a[x][y].first
signed main()
{
V = read(), n = read();
for(int i = 1; i <= n; i++) {
int tim = read(), val = read(), op = read();
if(!op) a[i][0].second = val * tim, a[i][0].first = tim;
else a[op][++cnt[op]].second = val * tim, a[op][cnt[op]].first = tim;
}
for(int i = 1; i <= n; i++) {
for(int j = V; j >= a[i][0].first; j--) {
if(j >= Tim(i, 0)) dp[j] = max(dp[j], dp[j - Tim(i, 0)] + Val(i, 0));
if(j >= Tim(i, 0) + Tim(i, 1)) dp[j] = max(dp[j], dp[j - Tim(i, 0) - Tim(i, 1)] + Val(i, 0) + Val(i, 1));
if(j >= Tim(i, 0) + Tim(i, 2)) dp[j] = max(dp[j], dp[j - Tim(i, 0) - Tim(i, 2)] + Val(i, 0) + Val(i, 2));
if(j >= Tim(i, 0) + Tim(i, 1) + Tim(i, 2)) dp[j] = max(dp[j], dp[j - Tim(i, 0) - Tim(i, 1) - Tim(i, 2)] + Val(i, 0) + Val(i, 1) + Val(i, 2));
}
}
cout << dp[V];
return (0 - 0);
}

P2340 [USACO03FALL]Cow Exhibition G

感觉是道比较精妙的分类讨论背包题?

我们将智商看做是体积,情商看做价值。

dpi,j 表示到了第 i 头牛,智商为 j 的最大情商数。

转移方程是 dpi,j=max{dpi1,j,dpjTimi+Vali}

这样做是会 MLE 的,我们考虑压掉一维,但是这题是有负数的,所以要将整个 dp 数组右移可能的最小智商和。

然后分类讨论来压掉一维:

  • Timi0 时,dpi,j=max{dpi1,j,dpjTimi+Vali},我们压掉一维是想让转移 dpj 的时候,

dpjdpjTimi 都未被转移过,所以倒叙枚举。

  • Timi<0 时, 正序枚举能保证用到的状态都是未被转移过的,也就是上一层的状态。
/**
* author: TLE_Automation
* creater: 2022.10.19
**/
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar
#define Tim(x) a[x].first
#define Val(x) a[x].second
using namespace std;
typedef long long ll;
const int qwq = 4e5;
const int N = 1e6 + 10;
const int mod = 998244353;
const ll inf = 0x3f3f3f3f3f3f3f3f;
#define debug cout << "i ak ioi" << "\n"
inline void print(int x) {if (x < 0) putchar('-'), x = -x; if(x > 9) print(x / 10); putchar(x % 10 + '0');}
inline char readchar() {static char buf[100000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;}
inline int read() { int res = 0, f = 0; char ch = gc();for (; !isdigit(ch); ch = gc()) f |= (ch == '-'); for (; isdigit(ch); ch = gc()) res = (res << 1) + (res << 3) + (ch ^ '0'); return f ? -res : res;}
/*
因为有负数,考虑将整个数组右移 1000*400
dp_j = dp_{j - v_i} + w_i
dp_j 表示智商为 j 的最大情商
*/
int n, dp[N << 1];
pair <int, int> a[N];
signed main()
{
n = read();
memset(dp, -0x3f, sizeof dp);
dp[qwq] = 0;
for(int i = 1; i <= n; i++) a[i].first = read(), a[i].second = read();
for(int i = 1; i <= n; i++) {
if(Tim(i) >= 0) {
for(int j = qwq << 1; j >= Tim(i); j--)
dp[j] = max(dp[j], dp[j - Tim(i)] + Val(i));
}
else {
for(int j = 0; j <= (qwq << 1) - Tim(i) ; j++)
dp[j] = max(dp[j], dp[j - Tim(i)] + Val(i));
}
}
int ans = -0x3f3f3f3f;
for(int i = qwq; i <= qwq << 1; i++) {
if(dp[i] >= 0) ans = max(dp[i] + i - qwq, ans);
}
cout << ans;
return (0 - 0);
}

P4059 [Code+#1]找爸爸

因为 g(k) 的性质可以看成是:

  • 如果是第一个空格,就 A

  • 不是第一个空格,就 B

我们设 dp[i][j][0/1/2] 分别表示匹配到了上面串的第 i 个位置,下面串的第 j 个位置,两个都没有空格,上面的有空格,下面的有空格。

转移的话就从对应的状态转移来。

注意一开始赋值极小值,因为空格的贡献是负的。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
constexpr int Maxn = 3e3 + 10;
constexpr ll inf = 0x3f3f3f3f3f3f3f3f;
ll dp[Maxn][Maxn][3];
char s[Maxn], t[Maxn];
int val[5][5], n, m, A, B;
inline int getpos(char s) {
if (s == 'A') return 1;
else if (s == 'T') return 2;
else if (s == 'G') return 3;u
else if (s == 'C') return 4;
else return 114514;
}
int main() {
scanf("%s %s", s + 1, t + 1);
n = strlen(s + 1), m = strlen(t + 1);
for (int i = 1; i <= 4; i++) for (int j = 1; j <= 4; j++) scanf("%d", &val[i][j]);
scanf("%d %d", &A, &B);
memset(dp, -0x3f, sizeof dp);
dp[0][0][0] = 0;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if(i && j) dp[i][j][0] = std::max(dp[i - 1][j - 1][0], std::max(dp[i - 1][j - 1][1], dp[i - 1][j - 1][2])) + val[getpos(s[i])][getpos(t[j])];
if(j) dp[i][j][1] = std::max(dp[i][j - 1][0] - A, std::max(dp[i][j - 1][1] - B, dp[i][j - 1][2] - A));
if(i) dp[i][j][2] = std::max(dp[i - 1][j][0] - A, std::max(dp[i - 1][j][1] - A, dp[i - 1][j][2] - B));
}
}
cout << std::max(dp[n][m][0], std::max(dp[n][m][1], dp[n][m][2]));
return 0;
}

P3146 [USACO16OPEN]248 G

dpl,r 为合并区间 [l,r] 的最大值。

#include<bits/stdc++.h>
using namespace std;
int dp[500][500], n, a[500];
int main() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
memset(dp, -0x3f, sizeof dp);
for(int i = 1; i <= n; i++) dp[i][i] = a[i];
for(int len = 2; len <= n; len++) {
for(int l = 1, r; l + len - 1 <= n; l++) {
r = l + len - 1;
for(int k = l; k <= r; k++)
if(dp[l][k] == dp[k + 1][r]) dp[l][r] = max(dp[l][r], dp[l][k] + 1);
}
}
int ans = -0x3f3f3f3f;
for(int l = 1; l <= n; l++) for(int r = 1; r <= n; r++) ans = max(ans, dp[l][r]);
cout << ans; return 0;
}

P3147 [USACO16OPEN]262144 P

上一题的加强版,设 dpi,j 表示以 j 为左端点合并出 i 的右端点的右侧。

dpi,j=dpi1,dpi1,j

#include<cstdio>
int dp[60][300010], n, ans;
int main() {
scanf("%d", &n);
for(int i = 1, x; i <= n; i++) scanf("%d", &x), dp[x][i] = i + 1;
for(int i = 2; i <= 58; i++)
for(int j = 1; j <= n; j++) {
if(!dp[i][j]) dp[i][j] = dp[i - 1][dp[i - 1][j]];
if(dp[i][j]) ans = i;
}
printf("%d", ans);
}

本文作者:TLE_Automation

本文链接:https://www.cnblogs.com/tttttttle/p/16602154.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   TLE_Automation  阅读(36)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起