P11188 解题报告
题目传送门
分享一下我做这道题是的心路历程。
首先感觉像是贪心,但是随便举了几个例子就推翻了,发现无论是先删掉
策略的选择如此复杂,考虑 dp。
其实很容易就能发现数据范围的异样:
因为
,而 ,所以选超过 个数进行操作二一定不如操作一优。
自此,我们可以将题意转化为:
给定一个序列
dp 的状态设计其实可以参考数据范围,设
一开始我想的是直接线性 dp,从前向后递推,同时记录下此时最优策略保留下来的数来辅助递推,但很快就发现连样例
错误
#include <cstring>
#include <iostream>
#define x first
#define y second
using namespace std;
const int N = 100010;
typedef long long ll;
typedef pair<ll, ll> PLL;
int cid, T;
int n;
char s[N];
int v[10];
PLL dp[N][6];
void solve() {
scanf("%s", s + 1);
n = strlen(s + 1);
for(int i = 1; i < 10; i++) scanf("%d", &v[i]);
ll sumcost = 0;
for(int i = 1; i <= n; i++)
sumcost += v[s[i] - '0'];
for(int i = 0; i <= n; i++)
for(int j = 0; j <= 5; j++)
dp[i][j] = {1e18, 0};
dp[0][0] = {0, 0};
for(int i = 1; i <= n; i++) {
int limit = min(i, 5);
dp[i][0] = dp[i - 1][0];
for(int j = 1; j <= limit; j++) {
dp[i][j] = dp[i - 1][j];
if(dp[i][j].x > dp[i - 1][j - 1].x + dp[i - 1][j - 1].y * 10 + s[i] - '0' - v[s[i] - '0'])
dp[i][j] = {dp[i - 1][j - 1].x + dp[i - 1][j - 1].y * 10 + s[i] - '0' - v[s[i] - '0'], dp[i - 1][j - 1].y * 10 + s[i] - '0'};
}
}
ll ans = 1e18;
for(int i = 0; i <= min(5, n); i++)
ans = min(ans, dp[n][i].x);
printf("%lld\n", sumcost + ans);
}
int main() {
scanf("%d%d", &cid, &T);
while(T--) {
solve();
}
return 0;
}
然后我就发现:正着 dp 是错的。
因为你正着 dp 的时候只会保留当前长度下最优的解,但它实际上是具有后效性的。
就是这组样例:
3158927982863528
41423 65081 37768 31661 5606 86055 71796 46535 92370
最优的策略是保留
但如果正着这样 dp,那么当考虑前
但是倒着 dp 就没有后效性了!
原因就是如果倒着做,每次更新时直接加上
那么新的状态定于就是:
状态转移方程:
最后答案就是选取
#include <cstring>
#include <iostream>
#define x first
#define y second
using namespace std;
const int N = 100010;
typedef long long ll;
int cid, T;
int n;
char s[N];
int v[10];
ll dp[N][6];
int power10[10];
void solve() {
scanf("%s", s + 1);
n = strlen(s + 1);
for(int i = 1; i < 10; i++) scanf("%d", &v[i]);
ll sumcost = 0;
for(int i = 1; i <= n; i++)
sumcost += v[s[i] - '0'];
for(int i = 0; i <= n + 1; i++)
for(int j = 0; j <= 5; j++)
dp[i][j] = 1e18;
dp[n + 1][0] = 0;
for(int i = n; i; i--) {
dp[i][0] = dp[i + 1][0];
int limit = min(5, n - i + 1);
for(int j = 1; j <= limit; j++) {
dp[i][j] = dp[i + 1][j];
dp[i][j] = min(dp[i][j], dp[i + 1][j - 1] + (s[i] - '0') * power10[j - 1] - v[s[i] - '0']);
}
}
ll ans = 1e18;
for(int i = 0; i <= min(5, n); i++)
ans = min(ans, dp[1][i]);
printf("%lld\n", sumcost + ans);
}
int main() {
scanf("%d%d", &cid, &T);
power10[0] = 1;
for(int i = 1; i < 10; i++)
power10[i] = power10[i - 1] * 10;
while(T--) {
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!