【动态规划】P11188 「KDOI-10」商店砍价|普及+

【动态规划】P11188 「KDOI-10」商店砍价|普及+

本文涉及知识点

C++动态规划

P11188 「KDOI-10」商店砍价

题目背景

English Statement. You must submit your code at the Chinese version of the statement.

您可以点击 这里 下载本场比赛的选手文件。

You can click here to download all tasks and examples of the contest.

密码 / Password:rAnHoUyaSuoBaoMimaNijuEdefAngsHa2)2$1)0(2@0!

本场比赛所有题目从标准输入读入数据,输出到标准输出。

题目描述

有一个正整数 n n n,保证其只由数字 1 ∼ 9 1\sim 9 1∼9 构成。

你可以做任意多次如下操作:

  • 选择 n n n 的一个数位 x x x,花费 v x v_x vx​ 的代价删除它,注意,此时 n n n 的数位个数会减少 1 1 1, n n n 的值也会发生相应的变化;
  • 或者,花费 n n n 的代价把剩余的所有数位删除。

求把整个数删除的最小代价。

输入格式

从标准输入读入数据。

本题有多组测试数据。

输入的第一行包含一个正整数 c c c,表示测试点编号。 c = 0 c=0 c=0 表示该测试点为样例。

第二行包含一个正整数 t t t,表示测试数据组数。

对于每组测试数据:

  • 第一行一个正整数 n n n,表示这个数的初始值。
  • 第二行九个正整数 v 1 , v 2 , … , v 9 v_1,v_2,\dots,v_9 v1​,v2​,…,v9​,表示删除每个数位的代价。

输出格式

输出到标准输出。

对于每组测试数据:

  • 输出一行一个正整数,表示最小代价。

输入输出样例 #1

输入 #1

0 3 123 10 10 10 10 10 10 10 10 10 1121 2 1 2 2 2 2 2 2 2 987654321 1 2 3 4 5 6 7 8 9 

输出 #1

21 6 45 

说明/提示

【样例 1 解释】

对于第一组测试数据,最优操作方案如下:

  • 删除数位 2 2 2,代价为 10 10 10,此时 n n n 变为 13 13 13;
  • 删除数位 3 3 3,代价为 10 10 10,此时 n n n 变为 1 1 1;
  • 删除 n n n 的剩余所有数位,代价为 1 1 1。

总代价为 10 + 10 + 1 = 21 10+10+1=21 10+10+1=21,可以证明,这是代价的最小值。

对于第二组测试数据,一种最优操作方案如下:

  • 删除第一个数位 1 1 1,代价为 2 2 2,此时 n n n 变为 121 121 121;
  • 删除最后一个数位 1 1 1,代价为 2 2 2,此时 n n n 变为 12 12 12;
  • 删除数位 2 2 2,代价为 1 1 1,此时 n n n 变为 1 1 1;
  • 删除 n n n 的剩余所有数位,代价为 1 1 1。

总代价为 2 + 2 + 1 + 1 = 6 2+2+1+1=6 2+2+1+1=6。

【样例 2】

见选手目录下的 bargain/bargain2.inbargain/bargain2.ans

这个样例满足测试点 3 ∼ 6 3\sim 6 3∼6 的约束条件。

【样例 3】

见选手目录下的 bargain/bargain3.inbargain/bargain3.ans

这个样例满足测试点 11 11 11 的约束条件。

【样例 4】

见选手目录下的 bargain/bargain4.inbargain/bargain4.ans

这个样例满足测试点 17 , 18 17,18 17,18 的约束条件。

【样例 5】

见选手目录下的 bargain/bargain5.inbargain/bargain5.ans

这个样例满足测试点 23 ∼ 25 23\sim 25 23∼25 的约束条件。


【数据范围】

对于全部的测试数据,保证:

  • 1 ≤ t ≤ 10 1\le t\le 10 1≤t≤10;
  • 1 ≤ n < 1 0 1 0 5 1\le n< 10^{10^5} 1≤n<10105;
  • 对于任意 1 ≤ i ≤ 9 1\le i\le 9 1≤i≤9, 1 ≤ v i ≤ 1 0 5 1\le v_i\le 10^5 1≤vi​≤105;
  • n n n 由数字 1 ∼ 9 1\sim 9 1∼9 构成。
测试点 n < n< n< v i ≤ v_i\le vi特殊性质
1 1 1 100 100 100 1 0 5 10^5 105
2 2 2 1 0 3 10^3 103 1 0 5 10^5 105
3 ∼ 6 3\sim 6 36 1 0 18 10^{18} 1018 1 0 5 10^5 105
7 ∼ 9 7\sim 9 79 1 0 40 10^{40} 1040 1 0 5 10^5 105
10 10 10 1 0 1 0 5 10^{10^5} 10105 1 0 5 10^5 105 n n n 由至多一种数字构成
11 11 11 1 0 1 0 5 10^{10^5} 10105 1 0 5 10^5 105 n n n 由至多两种数字构成
12 , 13 12,13 12,13 1 0 1 0 5 10^{10^5} 10105 1 0 5 10^5 105 n n n 由至多三种数字构成
14 ∼ 16 14\sim 16 1416 1 0 1 0 3 10^{10^3} 10103 1 0 5 10^5 105 v 1 = v 2 = v 3 = ⋯ = v 9 v_1=v_2=v_3=\dots =v_9 v1=v2=v3==v9
17 , 18 17,18 17,18 1 0 1 0 5 10^{10^5} 10105 1 0 5 10^5 105 v 1 = v 2 = v 3 = ⋯ = v 9 v_1=v_2=v_3=\dots =v_9 v1=v2=v3==v9
19 , 20 19,20 19,20 1 0 100 10^{100} 10100 100 100 100
21 , 22 21,22 21,22 1 0 1 0 3 10^{10^3} 10103 1 0 3 10^3 103
23 ∼ 25 23\sim 25 2325 1 0 1 0 5 10^{10^5} 10105 1 0 5 10^5 105

动态规划

性质一:最后删除前,数位一定不超过5位。如果超过5位,直接删除的成本是:
x × 1 0 i − 1 + y x\times 10^{i-1}+y x×10i−1+y,i是位数,x是最高位。删除最高位,再删除的成本是 v x + y v_x+y vx​+y,本题 v x ≤ 1 0 5 v_x \le10^5 vx​≤105,故i > 5时,删除最高位再删除时不劣解。
性质二:最后删除x,相当于节约 x 各位的成本 − x x各位的成本-x x各位的成本−x,删除所有位的成本- max ⁡ ( 节约的成本 ) \max(节约的成本) max(节约的成本)便是答案。

动态规划的状态表示

dp[n][j]表示处理完s的后n位,保留了j位节约的最大成本。 n ∈ [ 0 , N ] , j ∈ [ 0 , 5 ] n \in[0,N],j\in[0,5] n∈[0,N],j∈[0,5]。
空间复杂度: O(n)

动态规划的填表顺序

枚举前驱状态,和操作。n从0到N-1,j任意。

动态规划的转移方程

如果倒数第n各位数z不保留
dp[n+1][j] = dp[n][j]
如果保留:
d p [ n + 1 ] [ j + 1 ] = d p [ n ] [ j ] + z ∗ 1 0 j − v z dp[n+1][j+1] = dp[n][j] + z*10^j - v_z dp[n+1][j+1]=dp[n][j]+z∗10j−vz​

动态规划的初始值

dp[0][0] ,其它LLONG_MIN/2。

动态规划的范围值

sum - max(dp.back()) 。sum是直接删除所有位的成本和。

代码

核心代码

#include<iostream>#include<sstream>#include<vector>#include<map>#include<unordered_map>#include<set>#include<unordered_set>#include<string>#include<algorithm>#include<functional>#include<queue>#include<stack>#include<iomanip>#include<numeric>#include<math.h>#include<climits>#include<assert.h>#include<cstring>#include<list>#include<array>#include<bitset>#include<chrono>usingnamespace std::chrono;usingnamespace std;template<classT1,classT2> std::istream&operator>>(std::istream& in, pair<T1, T2>& pr){ in >> pr.first >> pr.second;return in;}template<classT1,classT2,classT3> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3>& t){ in >> get<0>(t)>> get<1>(t)>> get<2>(t);return in;}template<classT1,classT2,classT3,classT4> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3, T4>& t){ in >> get<0>(t)>> get<1>(t)>> get<2>(t)>> get<3>(t);return in;}template<classT1,classT2,classT3,classT4,classT5,classT6,classT7> std::istream&operator>>(std::istream& in, tuple<T1, T2, T3, T4, T5, T6, T7>& t){ in >> get<0>(t)>> get<1>(t)>> get<2>(t)>> get<3>(t)>> get<4>(t)>> get<5>(t)>> get<6>(t);return in;}template<classT=int> vector<T>Read(){int n; cin >> n; vector<T>ret(n);for(int i =0; i < n; i++){ cin >> ret[i];}return ret;}template<classT=int> vector<T>ReadNotNum(){ vector<T> ret; T tmp;while(cin >> tmp){ ret.emplace_back(tmp);if('\n'== cin.get()){break;}}return ret;}template<classT=int> vector<T>Read(int n){ vector<T>ret(n);for(int i =0; i < n; i++){ cin >> ret[i];}return ret;}template<int N =1'000'000>classCOutBuff{public:COutBuff(){ m_p = puffer;}template<classT>voidwrite(T x){int num[28], sp =0;if(x <0)*m_p++='-', x =-x;if(!x)*m_p++=48;while(x) num[++sp]= x %10, x /=10;while(sp)*m_p++= num[sp--]+48;AuotToFile();}voidwritestr(constchar* sz){strcpy(m_p, sz); m_p +=strlen(sz);AuotToFile();}inlinevoidwrite(char ch){*m_p++= ch;AuotToFile();}inlinevoidToFile(){fwrite(puffer,1, m_p - puffer,stdout); m_p = puffer;}~COutBuff(){ToFile();}private:inlinevoidAuotToFile(){if(m_p - puffer > N -100){ToFile();}}char puffer[N],* m_p;};template<int N =1'000'000>classCInBuff{public:inlineCInBuff(){}inline CInBuff<N>&operator>>(char& ch){FileToBuf();while(('\r'==*S)||('\n'==*S)||(' '==*S)){ S++;}//忽略空格和回车 ch =*S++;return*this;}inline CInBuff<N>&operator>>(int& val){FileToBuf();intx(0),f(0);while(!isdigit(*S)) f |=(*S++=='-');while(isdigit(*S)) x =(x <<1)+(x <<3)+(*S++^48); val = f ?-x : x; S++;//忽略空格换行 return*this;}inline CInBuff&operator>>(longlong& val){FileToBuf();longlongx(0);intf(0);while(!isdigit(*S)) f |=(*S++=='-');while(isdigit(*S)) x =(x <<1)+(x <<3)+(*S++^48); val = f ?-x : x; S++;//忽略空格换行return*this;}template<classT1,classT2>inline CInBuff&operator>>(pair<T1, T2>& val){*this>> val.first >> val.second;return*this;}template<classT1,classT2,classT3>inline CInBuff&operator>>(tuple<T1, T2, T3>& val){*this>> get<0>(val)>> get<1>(val)>> get<2>(val);return*this;}template<classT1,classT2,classT3,classT4>inline CInBuff&operator>>(tuple<T1, T2, T3, T4>& val){*this>> get<0>(val)>> get<1>(val)>> get<2>(val)>> get<3>(val);return*this;}template<classT=int>inline CInBuff&operator>>(vector<T>& val){int n;*this>> n; val.resize(n);for(int i =0; i < n; i++){*this>> val[i];}return*this;}template<classT=int> vector<T>Read(int n){ vector<T>ret(n);for(int i =0; i < n; i++){*this>> ret[i];}return ret;}template<classT=int> vector<T>Read(){ vector<T> ret;*this>> ret;return ret;}private:inlinevoidFileToBuf(){constint canRead = m_iWritePos -(S - buffer);if(canRead >=100){return;}if(m_bFinish){return;}for(int i =0; i < canRead; i++){ buffer[i]= S[i];//memcpy出错 } m_iWritePos = canRead; buffer[m_iWritePos]=0; S = buffer;int readCnt =fread(buffer + m_iWritePos,1, N - m_iWritePos,stdin);if(readCnt <=0){ m_bFinish =true;return;} m_iWritePos += readCnt; buffer[m_iWritePos]=0; S = buffer;}int m_iWritePos =0;bool m_bFinish =false;char buffer[N +10],* S = buffer;};classSolution{public:longlongAns(const string& s, vector<int>& v){constint N = s.length(); vector<longlong>pre(6, LLONG_MIN /2); pre[0]=0; vector<int> p10 ={1,10,100,1000,10'000,100'000};for(int n =0; n < N; n++){constint z = s[N -1- n]-'0';auto cur = pre;//不保留for(int j =0; j +1<6; j++){ cur[j +1]=max(cur[j +1], v[z -1]- p10[j]* z + pre[j]);} pre.swap(cur);}longlong sum =0;for(constauto& ch : s){ sum += v[ch -'1'];}return sum -*max_element(pre.begin(), pre.end());}};intmain(){#ifdef_DEBUGfreopen("a.in","r",stdin);#endif// DEBUG  ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob;int c, t; cin >> c >> t; string s;for(int i =0; i < t; i++){ cin >> s;auto v = Read<int>(9);#ifdef_DEBUG //printf("M=%d", M);Out(s,",s=");Out(v,",v=");//Out(ss, ",ss=");//Out(ope, ",ope=");#endif// DEBUG auto res =Solution().Ans(s,v); cout << res <<"\n";}return0;}

单元测试

TEST_METHOD(TestMlethod11){auto res =Solution().Ans("9", vector<int>{1,1,1,1,1,1,1,1,1});AssertEx(1LL, res);}TEST_METHOD(TestMlethod12){ s ="123", v ={10,10,10,10,10,10,10,10,10};auto res =Solution().Ans(s, v);AssertEx(21LL, res);}TEST_METHOD(TestMlethod13){ s ="1121", v ={2,1,2,2,2,2,2,2,2};auto res =Solution().Ans(s, v);AssertEx(6LL, res);}TEST_METHOD(TestMlethod14){ s ="987654321", v ={1,2,3,4,5,6,7,8,9};auto res =Solution().Ans(s, v);AssertEx(45LL, res);}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步ZEEKLOG学院,听白银讲师(也就是鄙人)的讲解。
https://edu.ZEEKLOG.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.ZEEKLOG.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

Read more

从零开始“养龙虾”:OpenClaw 本地极简部署与 QQ 机器人接入全保姆级教程

从零开始“养龙虾”:OpenClaw 本地极简部署与 QQ 机器人接入全保姆级教程

文章目录 * 引言 * 什么是 OpenClaw? * 为什么选择 OpenClaw? * 一、基础环境准备 * 1. 安装 Node.js (v22及以上) * 2.安装 Git * 3. 解决 npm 被拦截(没报错跳过) * 二、一键部署与唤醒“龙虾” * 1.全自动拉取与组装 * 2.醒龙虾与配置“大脑” * 三、接入官方 QQ 机器人(可选) * 1. 领取官方机器人的“身份证” * 2. 本地安装专属通信插件 * 3. 结果展示 * 总结 引言 什么是 OpenClaw? 最近开源界有一只“红皮小龙虾”非常火,它就是 OpenClaw。

By Ne0inhk
基于深度学习的无人机航拍小目标检测算法研究

基于深度学习的无人机航拍小目标检测算法研究

本项目针对无人机航拍场景下的小目标检测问题,基于 YOLO11 系列模型,在 VisDrone 2019 数据集上进行训练与优化,并提供了完整的检测系统桌面应用,支持图片、视频、摄像头的实时检测与训练指标可视化。 一、项目概述 无人机航拍图像具有目标尺度小、密集分布、多尺度混合等特点,传统检测算法难以取得理想效果。本项目采用 Ultralytics YOLO11 框架,结合 VisDrone 数据集进行训练,实现了对行人、车辆等 10 类交通相关目标的高效检测,并配套开发了基于 PyQt6 的桌面应用,便于模型验证与日常使用。 二、数据集 2.1 数据集简介 本项目使用 VisDrone 2019-DET 数据集,由天津大学机器学习与数据挖掘实验室 AISKYEYE 团队发布,对应 ICCV 2019 "Vision

By Ne0inhk
树莓派4B连接大疆M300无人机全网最细教程

树莓派4B连接大疆M300无人机全网最细教程

注:本教程适用于树莓派4B连接大疆M300_RTK无人机,若是其余型号可以参考本文思路,但是具体细节请前往官方教程或大疆开发者论坛查找,第三方开发板连接大疆无人机,不同型号之间会有很多细节差异,请确认自己的型号然后针对性查找 官方教程网址:Payload SDK (官方的是树莓派4B连接M350!并非M300,实现细节完全不同,请慎重查看) 大疆开发者论坛网址:Payload SDK – 大疆创新SDK技术支持论坛 (优点:几乎能找到所有问题的解决方法;缺点:太零散了,找解决方法如同大海捞针) 1 硬件准备 1.1 硬件选型 * 无人机型号:M300_RTKM300顶部一共有三个接口,其中OSDK端口和云台口(Payload SDK Port)可以用来运行PSDK程序,TypeC调参口,则是用来与电脑连接,打开DJI Assistant2软件后,可以升级无人机固件,导出日志,使用模拟器,绑定负载等。 1.FPV摄像头13.左视和右视红外感知系统25.调参接口2.前视红外感知系统14.

By Ne0inhk
基于 LangChain 实现数据库问答机器人

基于 LangChain 实现数据库问答机器人

基于 LangChain 实现数据库问答机器人 * 一、简介 * 二、应用场景 * 三、实战案例 * 1、需求说明 * 2、实现思路 * 3、对应源码 一、简介 在 Retrieval 或者 ReACT 的一些场景中,常常需要数据库与人工智能结合。而 LangChain 本身就封装了许多相关的内容,在其官方文档-SQL 能力中,也有非常好的示例。 二、应用场景 在未出现人工智能,如果想要完成数据查询与数据分析的工作,则需要相关人员有相应的数据库的功底,而在 LangChain 结合大语言模型的过程中,应对这些问题则相当轻松——写清晰的提示词即可。 * 生成将基于自然语言问题运行的查询。 在传统的工作流程中,如果想要在数据库中搜索一些信息,那么就必须要掌握相应的数据库技术,比如 SQL 语句查询等,但是其本身有很高的学习成本。如果能用自然语言代替这个过程,则任何人都无需学习 SQL

By Ne0inhk