【难度】
3.5
/
10
3.5/10
3.5/10
其实很简单的区间dp
但是当时没有时间想就没做出来//
【题意】
让
S
S
S是一个无限的串“1145141919”拼接在一起,成为
“
114514191911451419191145141919
⋯
”
“114514191911451419191145141919\cdots”
“114514191911451419191145141919⋯”
选择
S
S
S 的一个前缀
T
T
T,你可以在
T
T
T 中插入
′
(
′
′
)
′
′
+
′
′
∗
′
'('\quad ')'\quad '+'\quad '*'
′(′′)′′+′′∗′符号,形成新的串
T
′
T'
T′
v
a
l
(
T
′
)
val(T')
val(T′) 的值符合十进制的一般运算规则
现在给你一个数字
N
N
N,让你求出最小长度的
T
,
s
.
t
.
v
a
l
(
T
′
)
=
N
T,s.t.\quad val(T')=N
T,s.t.val(T′)=N
如果没有这样的串
T
T
T ,则输出 -1
【数据范围】
1 ≤ N ≤ 5000 1\le N\le 5000 1≤N≤5000
【样例输入】
T
N
3
520
1
2
【样例输出】
6
1
2
【解释】
N=520时:
T
=
114514
,
T
′
=
1
+
1
+
4
+
514
,
v
a
l
(
T
′
)
=
520
T=114514,T'=1+1+4+514,val(T')=520
T=114514,T′=1+1+4+514,val(T′)=520
【思路】
一般看到数据范围特别小的题,都是可以打表处理的。
既然想到里面括号可以任意添加,那么对于两个相邻的串
S
[
i
,
k
]
与
S
[
k
+
1
,
j
]
S[i,k]与S[k+1,j]
S[i,k]与S[k+1,j]
若
S
[
i
,
k
]
S[i,k]
S[i,k]能计算出答案
v
a
l
S
[
i
,
k
]
∈
A
val_{S[i,k]}\in\mathbb{A}
valS[i,k]∈A
若
S
[
k
+
1
,
j
]
S[k+1,j]
S[k+1,j]能计算出答案
v
a
l
S
[
k
+
1
,
j
]
∈
B
val_{S[k+1,j]}\in\mathbb{B}
valS[k+1,j]∈B
那么容易得到
S
e
t
[
i
,
j
]
=
{
v
a
l
∣
v
a
l
=
p
+
q
o
r
v
a
l
=
p
×
q
,
其
中
p
∈
A
并
且
q
∈
B
}
Set[i,j]=\{val \mid val=p+q \quad or\quad val=p\times q\,,其中p\in\mathbb{A} \,并且\,q\in \mathbb{B}\}
Set[i,j]={val∣val=p+qorval=p×q,其中p∈A并且q∈B}
其他一些细节处理:
注意对于一个区间
[
i
,
j
]
[i,j]
[i,j] 会存在多个不同的值,用
s
e
t
\pmb{set}
setsetset 存储。
注意数字大于5000时,因为没有减号操作,因此数字不会变小,便不要插入到集合中去
【预处理代码】
这里
n
n
n 表示枚举的S数组的长度,不表示题目所给的
N
N
N
时间复杂度
o
(
n
3
×
∣
S
e
t
[
i
,
k
]
∣
×
∣
S
e
t
[
k
+
1
,
j
]
∣
1
≤
i
≤
k
≤
j
≤
n
)
o(n^3\times\underset{1\,\le \,i\le \,k\le \,j\le \,n}{\Big| Set[i,k]\Big|\times\Big |Set[k+1,j] \Big |})
o(n3×1≤i≤k≤j≤n∣∣∣Set[i,k]∣∣∣×∣∣∣Set[k+1,j]∣∣∣)
我这里选
n
=
20
n=20
n=20 基本跑一分钟也能跑完
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
const int MAX = 20; ///其实只要枚举S串长12即可,稍微往大开
set<int>S[MAX][MAX]; ///区间DP的set
int fir[5050]; ///答案数组
int tmp[MAX]; ///恶臭的S数组
void init(){
for(int i=1;i<=5000;++i)fir[i]=INF;
tmp[1]=tmp[2]=tmp[5]=tmp[7]=tmp[9]=1;
tmp[3]=tmp[6]=4;
tmp[4]=5;
tmp[8]=tmp[10]=9;
for(int i=11;i<MAX;++i)tmp[i] = tmp[i-10]; ///恶臭的S数组循环一下
for(int L=1;L<=4;++L){ ///首先处理区间S[i,j]不加运算符的值
for(int i=1;i<MAX-L+1;++i){
int j = i + L - 1;
int t = 0;
for(int k=i;k<=j;++k){
t*=10;
t+=tmp[k];
}
if(t>5000)continue;
S[i][j].insert(t);
if(i==1)fir[t]=min(fir[t],j);
}
}
}
int main(void){
init();
for(int L=2;L<MAX;++L){ ///区间DP 枚举区间长度
for(int i=1;i<MAX-L+1;++i){ ///区间DP 枚举区间起点
int j = i + L - 1; /// 重点
for(int k=i;k<j;++k){ /// 枚举两个区间的分界
for(auto &It_i : S[i][k]){
for(auto &It_j : S[k+1][j]){
int add = It_i + It_j;
int mul = It_i * It_j;
if(add<=5000){ /// 注意剪枝
S[i][j].insert(add);
if(i==1)fir[add]=min(fir[add],j); /// 只有i=1才是前缀答案
}
if(mul<=5000){ /// 注意剪枝
S[i][j].insert(mul);
if(i==1)fir[mul]=min(fir[mul],j);
}
}
}
}
}
}
for(int i=1;i<=5000;++i){ /// 无解应该输出-1 输出在文件中即可打表完成
printf("%d,",fir[i]==INF ? -1 : fir[i]);
}
}