H y p e r l i n k Hyperlink Hyperlink
https://www.luogu.com.cn/problem/P1018
D e s c r i p t i o n Description Description
给定一个位数为 n n n的正整数,要求在其中添加 m m m个乘号,使得这 m + 1 m+1 m+1个部分的乘积最大
数据范围: n ≤ 40 , m ≤ 6 n\leq 40,m\leq 6 n≤40,m≤6
S o l u t i o n Solution Solution
暴力枚举每个乘号放哪里显然是会 T T T的,这样的复杂度是 O ( n m ) O(n^m) O(nm)
考虑区间
d
p
dp
dp,设
f
[
i
]
[
k
]
f[i][k]
f[i][k]表示前
i
i
i个数,放了
k
k
k个乘号的最大乘积
显然
f
[
i
]
[
0
]
=
a
[
i
]
[
0
]
f[i][0]=a[i][0]
f[i][0]=a[i][0](
a
[
i
]
[
j
]
a[i][j]
a[i][j]表示第
i
i
i位开始到第
j
j
j位构成的数,这可以
n
2
n^2
n2预处理,当然也可以边转移边算,这样复杂度多一个
n
n
n)
对于每放入一个乘号,考虑它放入的位置,设其放在
j
j
j的后面,则有
f
[
i
]
[
k
]
=
m
a
x
{
f
[
j
]
[
k
−
1
]
×
a
[
j
+
1
]
[
i
]
}
f[i][k]=max\{f[j][k-1]\times a[j+1][i]\}
f[i][k]=max{f[j][k−1]×a[j+1][i]}
表示在
j
j
j后面放一个乘号,和
[
j
+
1
,
i
]
[j+1,i]
[j+1,i]这部分的数乘在一起,则构成了对
f
[
i
]
[
k
]
f[i][k]
f[i][k]的转移
终态: f [ n ] [ m ] f[n][m] f[n][m]
这样的做法复杂度为 O ( n 3 ) O(n^3) O(n3)【预处理 a a a的前提下,不预处理的话多一个 n n n】,结合 l o n g l o n g long\ long long long能拿到 60 p t s 60pts 60pts的好成绩
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
char s[41];int n,m;
LL a[41][41],f[41][7];
int main()
{
scanf("%d%d\n",&n,&m);
scanf("%s",s+1);
for(register int i=1;i<=n;i++)
{
a[i][i]=s[i]-48;
for(register int j=i+1;j<=n;j++) a[i][j]=a[i][j-1]*10+s[j]-48;
}
for(register int i=1;i<=n;i++) f[i][0]=a[1][i];
for(register int k=1;k<=m;k++)
for(register int i=k+1;i<=n;i++)
for(register int j=k;j<i;j++)
f[i][k]=max(f[i][k],f[j][k-1]*a[j+1][i]);
printf("%lld",f[n][m]);
}
没有满分的原因是因为没加高精度,我们用结构体可以使代码简洁些
压位高精只是个人喜好,因为这样跑得快,数组也可以开得更小,当然你也可以不压位
带上高精度之后的复杂度是 O ( n 3 L e n 2 ) O(n^3Len^2) O(n3Len2), L e n Len Len表示高精度数组的大小
C o d e Code Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
char s[41];int n,m;
struct int233
{
int t[41];
int233(){memset(t,0,sizeof(t));}//写这一行是为了懒得清空
}a[41][41],f[41][41];//定义大整数
inline int233 Mul_1(int233 A,int x)//高精乘单精
{
int g=0;
for(register int j=1;j<=40;j++)
{
g=A.t[j]*x+g;
A.t[j]=g%10000;
g/=10000;
}
return A;
}
inline int233 Add(int233 A,int x)//高精加高精
{
int g=0;
A.t[1]+=x;
for(register int j=1;j<=40;j++)
{
g=A.t[j]+g;
A.t[j]=g%10000;
g/=10000;
}
return A;
}
inline int233 Mul_2(int233 A,int233 B)//高精乘高精
{
int233 C;
for(register int i=1;i<=20;i++)
for(register int j=1;j<=20;j++)
{
C.t[i+j-1]=C.t[i+j-1]+A.t[i]*B.t[j];
C.t[i+j]=C.t[i+j]+C.t[i+j-1]/10000;
C.t[i+j-1]=C.t[i+j-1]%10000;
}
return C;
}
inline void print(int233 x)//输出高精度数组
{
int j=40;
while(!x.t[j]&&j>1) j--;printf("%d",x.t[j]);
for(register int i=j-1;i;i--) printf("%04d",x.t[i]);
return;
}
inline bool Compare(int233 A,int233 B)//比较A,B的大小,A大返回True
{
for(register int i=40;i;i--) if(A.t[i]!=B.t[i]) return A.t[i]>B.t[i];
return false;
}
int main()
{
scanf("%d%d\n",&n,&m);
scanf("%s",s+1);
for(register int i=1;i<=n;i++)
{
a[i][i].t[1]=s[i]-48;
for(register int j=i+1;j<=n;j++)
{
a[i][j]=Mul_1(a[i][j-1],10);//乘十
a[i][j]=Add(a[i][j],s[j]-48);//加上去
}
}
for(register int i=1;i<=n;i++) f[i][0]=a[1][i];
for(register int k=1;k<=m;k++)
for(register int i=k+1;i<=n;i++)
for(register int j=k;j<i;j++)
{
int233 tmp=Mul_2(f[j][k-1],a[j+1][i]);//高精乘高精
if(Compare(tmp,f[i][k])) f[i][k]=tmp;//转移
}
print(f[n][m]);//输出
}