title: DP优化
tags: [DP, DP优化]
categories: [OI, 算法专题]
ABC415G Get Many Cola
原题 · 标签:#DP #DP优化
考点
分析性质题
题意
- 初始有 \(N\) 瓶可乐,空瓶数为 \(0\)。可重复执行以下操作任意次:
- 喝掉 \(1\) 瓶可乐(需有瓶装可乐),瓶装可乐 \(-1\),空瓶 \(+1\)。
- 选择一种兑换方案 \(i\)(\(1 \le i \le M\)),用 \(A_i\) 个空瓶换 \(B_i\) 瓶可乐(需空瓶足够)。
- 求最多能喝到的可乐总数。
数据范围:\(1 \le N \le 10^{15}\),\(1 \le M \le 2 \times 10^5\),\(1 \le B_i < A_i \le 300\)。
思路
首先考虑DP式子。DP[i] = j表示有\(i\)瓶可乐实际上能喝\(j\)瓶。当\(i\)大于等于\(a_i\)时,将行为看做物品,体积为\(a_i - b_i\),价值为\(a_i\)。
发现\(a_i\),\(b_i\)很小。考虑我们可以一直选择性价比最高的方案直到>=\((max a_i) ^ 2\)。因为在这之前我们肯定可以选到杂碎的方案。
所以实际上预处理DP[i]到\(90000\)就可以了。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define f(i,a,b) for(int i=(a);i<=(b);i++)
#define Dl(a) cout << #a << " : " << a << "\n";
#define D(a) cout << #a << " : " << a;
#define Da(a,i,j) cout << #a << " : ";f(idx,i,j){cout << a[idx] <<" ";}
const int N = 3e2+10;
int wu[N];
int F[N*N];
int n,m;
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin >> n >> m;
f(i,1,m){
int a,b;cin >> a >> b;
wu[a] = max(b,wu[a]);
}
f(i,1,90000){
F[i] = i;
f(j,1,300){
if (i >= j){
F[i] = max(F[i],F[i - j + wu[j]] + j);
}
}
}
// Da(wu,1,5);
// Da(F,1,5);
if (n <= 90000){
cout << F[n];
return 0;
}
double mx = -10000;
int idx = 0;
f(i,1,300){
int V = i - wu[i];
int W = wu[i];
if (!W) continue;
if (mx < W * 1.0 / V){
mx = W * 1.0 / V;
idx = i;
}
}
// Dl(idx);
int V = idx - wu[idx];
int W = idx;
int u = (n - 90000) / V + 1;
int ans = F[n-u*V] + u * W;
cout << ans <<"\n";
return 0;
}
P1545 USACO04DEC Dividing the Path G
原题 · 标签:#DP #DP优化 #单调队列
考点
单调队列优化+差分处理限制
题意
- 在 \([0, L]\) 的数轴上放置喷灌器,每个喷灌器位于整数坐标,射程 \(x\) 为整数且 \(A \le x \le B\),灌溉范围为 \([i-x, i+x]\),必须在 \([0, L]\) 内。
- 要求整个 \([0, L]\) 被完全覆盖,且喷灌器的灌溉区域不重叠。
- 有 \(N\) 个奶牛喜爱的草区 \([S_i, E_i]\),每个草区必须被恰好一个喷灌器完整覆盖。
- 求最少需要的喷灌器数量,若无解输出 \(-1\)。
数据范围:\(1 \le L \le 10^6\)(\(L\) 为偶数),\(1 \le A \le B \le 10^3\),\(1 \le N \le 10^3\)。
思路
发现最优解一定有使得所有放喷灌的点是偶数的情况,所以只在偶数位DP就可以。
不重叠的问题,用一个ban数组维护。发现开区间内必不能放喷灌。
发现区间内要维护i-2b ~ i-2a的dp最小值。想到单调队列。
代码
#include <bits/stdc++.h>
using namespace std;
//#define int long long
#define pii pair<int,int>
#define f(i,a,b) for(int i=(a);i<=(b);i++)
#define Dl(a) cout << #a << " : " << a << "\n";
#define D(a) cout << #a << " : " << a;
#define Da(a,i,j) cout << #a << " : ";f(idx,i,j){cout << a[idx] <<" ";}
const int N = 1e6+10;
int n,l,a,b;
int d[N],ban[N];
int dp[N];
deque<int> q;
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin >> n >> l >> a >> b;
f(i,1,n){
int s,e; cin >>s >> e;
d[s+1] ++;d[e] --;
}
f(i,1,l){
ban[i] = ban[i-1] + d[i];
}
memset(dp,0x3f,sizeof dp);
dp[0] = 0;
for (int i=2;i<=l;i+=2){
// 维护单调队列
int lef = max(0,i-2*b),rig = max(0,i-2*a);
// 弹出过期队首
while (!q.empty() && q.front() < lef){
q.pop_front();
}
int j = rig;
if (!ban[j] && i-2*a >= 0){
while (!q.empty() && dp[q.back()] >= dp[j]){
q.pop_back();
}
q.push_back(j);
}
if (ban[i]) continue;
// D(i);Dl(ban[i]);
if (!q.empty()) dp[i] = dp[q.front()] + 1;
// D(i);Dl(dp[i]);
}
if (dp[l] >= 0x3f3f3f3f) cout << -1;
else cout << dp[l];
return 0;
}
CF1854B Earn or Unlock
原题 · 标签:#DP
考点
bitset优化
题意
- 有 \(n\) 张牌从上到下排列,第 \(i\) 张数值为 \(a_i\)。初始只有第一张牌未锁定,其余锁定。
- 每回合选择一张未锁定牌(数值为 \(v\)),执行以下操作之一后将其移除:
- 解锁从顶部开始的前 \(v\) 张锁定牌(不足则全解锁)。
- 获得 \(v\) 点胜利分数。
- 当没有未锁定牌或牌堆为空时游戏结束。求最大胜利分数。
数据范围:\(1 \le n \le 10^5\),\(0 \le a_i \le n\)。
思路
很容易想到,分数是总数-用于解锁的分数。
考虑在当前位解锁实际上是将当前可行性左移 \(v\) 位,用bitset解决正好。
需要注意处理到i时,dp i要最后设为0。因为其对应到 i 位结束。
P11233 CSP-S 2024 染色
原题 · 标签:#DP #DP优化
考点
分析性质设计DP
浙公网安备 33010602011771号