【普及组_在线赛】最小与最大
描述
题目描述
做过了乘积最大这道题,相信这道题也难不倒你。
已知一个数串,可以在适当的位置加入乘号(设加了k个,当然也可不加,即分成k+1个部分),设这k+1个部分的乘积(如果k=0,则乘积即为原数串的值)对m 的余数(即mod m)为x;
现求x能达到的最小值及该情况下k的最小值,以及x能达到的最大值及该情况下的k的最小值(可以存在x的最小值与最大值相同的情况)。输入
第一行为数串,长度为n 满足2<=n<=1000,且数串中不存在0;
第二行为m,满足2<=m<=50。输出
四个数,分别为x的最小值 和 该情况下的k,以及x的最大值和 该情况下的k,中间用空格隔开。
样例输入
4421
22样例输出
0 1 21 0
分析
这题看着像数论,实际上是DP。
首先要知道,做这题完全无须高精度(包括暴力)。
借鉴一下人人皆知的读入优化:
如果有个数为a,你要在末尾安上一个b,那么得出的值是
然后直接取模。
这题m很小,是AC的关键。
设
什么意思?
我们枚举i时,前面有些数时分好的,但是还有一段你要继续在后面安数,这一段就是没分好的。
初始化:
如何从
考虑两种情况:
1. k后面安上第
2. 在i和
求出所有
然后统计答案就行了。
注意,因为这是段数,所以最后记得
这样就可以简单粗暴地AC了。
时间复杂度
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char str[1007];
int m;
int f[1001][51][51];
int main()
{
freopen("minmax.in","r",stdin);
freopen("minmax.out","w",stdout);
scanf("%s",str+1);//字符串读入
int len=strlen(str+1);
scanf("%d",&m);
int i,j,k;
f[1][1][(str[1]-'0')%m]=1;//初始化
int *q,*p;
for (i=1;i<len;++i)
for (j=0;j<=m;++j)
for (k=0;k<=m;++k)
{
q=&f[i][j][k];//利用指针优化寻址
if (*q)
{
p=&f[i+1][j][(k*10+str[i+1]-'0')%m];
if (*p)
*p=min(*p,*q);
else
*p=*q;
p=&f[i+1][j*k%m][(str[i+1]-'0')%m];
if (*p)
*p=min(*p,*q+1);
else
*p=*q+1;
}
}
int s,mins=2147483647,mins_k,maxs=-2147483648,maxs_k;
for (i=0;i<=m;++i)//统计答案
for (j=0;j<=m;++j)
{
q=&f[len][i][j];
if (*q)
{
s=i*j%m;
if (s<mins)
{
mins=s;
mins_k=*q;
}
else if (s==mins)
mins_k=min(mins_k,*q);
if (s>maxs)
{
maxs=s;
maxs_k=*q;
}
else if (s==maxs)
maxs_k=min(maxs_k,*q);
}
}
printf("%d %d %d %d
",mins,mins_k-1,maxs,maxs_k-1);
}
注意事项
- 为什么我要设段数?因为
段数>0 ,所以不用一开始赋特殊值。 - 为什么用指针?优化寻址,加快程序速度
- 题外话:我第一次交时RE,95分。调好久后发现是枚举i时将
<len 打成<=len ,好尴尬! - 题外话:我后来赋初值减少了些if语句,不知为何却慢了;我还用队列优化一下DP,存有用状态也更慢了。我第一次AC时f的每个节点多设了一个标记,但没必要。
- 我的方法跟别人不同,他们设两维状态。但是他们时间复杂度
O(n2m) ,我的O(nm2) ,明显更优。