题目描述
想要背诵一段很长的数字,比如圆周率,可以将这些数字分割成一个个片段,根据每个片段的规律做不同的记忆训练。不妨假定每个片段最少 3 个数,最多 6 个数,各种片段的记忆难度定义如下:
- 若所有数字相同,如
666
,88888
等,则记忆难度为 1; - 公差为 11 或者 −1−1 的等差数列,如
3456
,43210
等,记忆难度为 2; - 两个数字交替出现,如
313
,68686
等,记忆难度为 4; - 其他等差数列,如
7531
,369
等,记忆难度为 5,但注意36912
不算等差数列,因为我们只考虑一位数字; - 不属于上述任何一条规律的,比如
2794
等,记忆难度为 10。
现在给定一个由数字构成的字符串 s,请找出一个分割片段的方法,使得所有片段的记忆难度之和最小。
输入格式
一个由数字构成的字符串 s。
输出格式
单个整数:表示记忆难度之和的最小值。
数据范围
设 s 的长度为 n,则
#对 30% 的数据 3≤n≤2e1;
#对 60% 的数据 3≤n≤1e4;
#对 100% 的数据 3≤n≤1e5。
样例数据
输入:
314159265358979323846
输出:
34
说明:
分成(314159)(265358)(979)(323846),总难度为10+10+4+10=34
输入:
271828182845904523536
输出:
40
说明:
分成(271828)(182845)(904523)(536),总难度为40
输入:
12122222
输出:
5
说明:
分成(1212)(2222),总难度4+1=5
思路:
是一道区间dp问题。设dp[i]为前i个数的记忆难度(3=<i<=n),明显其记忆难度可以由
dp[i-k]+dif(i,k)得到(3=<k<=6,i>=k),dif(i,k)为第i个数字为开始往前k长度这个区间范围的记忆难度。枚举k=3~6, 取最小的即可。即状态转移方
程为:dp[i]=min(dp[i],dp[i-k]+dif(i,k))。
最后答案就是dp[n]
/*分析:
是一道区间dp问题。设dp[i]为前i个数的记忆难度(3=<i<=n),明显其
难度可以由dp[i-k]+dif(i,k)得到(3=<k<=6),dif为第i个数字为开始往前
k长度这个区间范围的记忆难。枚举k=3~6, 取最小的即可。即状态转移方
程为:dp[i]=min(dp[i],dp[i-k]+dif(i,k)
最后答案就是dp[n]
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=100005;
const long long INF=1e18;
long long dp[maxn];
string str;
long long dif(int end,int l){
long long val[5]={1,2,4,5,10};
end--;
int gc=str[end]-str[end-1];
int cnt=0;
for(int i=end-1;i>=(end-l+1);i--){
if(str[i]!=str[i+1]){//全数字相等待情况
val[0]=0;
}if((str[i+1]-str[i])!=gc){//等差数列
val[1]=0;val[3]=0;
}else{
if(abs(gc)==1){val[3]=0;}
else{val[1]=0;}
}cnt++;
if(cnt>1&&str[i]!=str[i+2]){//两个交替数字
val[2]=0;
}
}for(int i=0;i<5;i++){
if(val[i])return val[i];
}
}int main(int argc, char** argv) {
cin>>str;
int len=str.length();
for(int i=1;i<=len;i++){
dp[i]=INF;
for(int j=3;j<=6&&(i-j)>=0;j++){
dp[i]=min(dp[i],dp[i-j]+dif(i,j));
}
}cout<<dp[len];return 0;
}