题解 CF1487F Ones
题意
给定一个正整数 \(n\),要求用只由 \(1\) 组成的数字的和或差表示 \(n\),且使用 \(1\) 个数最少。
例如,\(24 = 11 + 11 + 1 + 1\),\(102 = 111 - 11 + 1 + 1\) 是两种使用 \(1\) 个数最少的方案。
解法
题目等价于用只由 \(1\) 组成的数字来抵消 \(n\),最终使 \(n\) 变为 \(0\)。注意到 \(n\) 的范围只有 \(50\) 位,不难想到用一个朴素的 DP 来解决。
设 \(n\) 有 \(l\) 位,最高位为第 \(1\) 位,最低位为第 \(l\) 位,则在第 \(i\) 位时想要 \(-1\) 或 \(+1\),第 \(i+1 \sim l\) 位也都要相应地 \(-1\) 或 \(+1\)。
同时,在第 \(i\) 位上的变化不可能既有 \(-1\) 又有 \(+1\),因此我们可以记录第 \(i\) 位的变化是 \(-1\) 还是 \(+1\)。
记 \(f[pos][c][x][delta(0/1)]\) 表示当前从高位往低位考虑到第 \(pos\) 位,前 \(pos\) 位还未抵消的和为 \(c\),后 \(l-pos\) 位都要减去一个数 \(x\),当前变化是 \(delta\)(\(-1\) 或 \(+1\))时最少要用的 \(1\) 的个数。
那么,\(f[pos][c][x][delta]=min(f[pos][c][x+(delta? 1:-1)][delta]+l-pos,f[pos+1][c*10+ch[pos+1]-'0'-x][x][1],f[pos+1][c*10+ch[pos+1]-'0'-x][x][0]))\)。
-
由 \(f[pos][c][x+(delta? 1:-1)][delta]+l-pos\) 转移来是保持当前位 \(pos\) 和变化量 \(delta\) 不变,在第 \(pos\) 位之后全加上 \(delta\)。
-
由 \(f[pos+1][c*10+ch[pos+1]-'0'-x][x][1]\) 和 \(f[pos+1][c*10+ch[pos+1]-'0'-x][x][0]\) 转移来是让当前位 \(pos\) 后移一位,枚举变化量 \(delta\)。
同时,我们可以确定 \(c\) 和 \(x\) 的上下界。
由于在第 \(i\) 位时加 \(6\) 个 \(1\) 要 \(6 \times (l-i+1)\) 个 \(1\),而在第 \(i-1\) 位加 \(1\) 个 \(1\),在第 \(i\) 位减 \(4\) 个 \(1\),再在第 \(l\) 位减 \(1\) 个 \(1\) 要 \(5 \times (l-i)+2\) 个 \(1\),效果相同且前者用的 \(1\) 一定比后者多,故没有位上用的 \(1\) 会大于等于 \(6\) 个。
我们需要在 \(l\) 位的基础上再往前考虑 \(1\) 位,因此每位用的 \(1\) 不超过 \(5 \times (l+1)\),即 \(x \le 5 \times (l+1)\)。
再由转移方程知 \(c \times 10-x \le \dfrac{5 \times \sum_{i=1}^{l} \underbrace{1 \cdots 1}_{i}}{10^{l-1}} \le \dfrac{500}{81}\),故 \(c \le \dfrac{x}{10}+1\)。
用记忆化 DP 即可。
Code
#include<cstdio>
#include<cstring>
#define inf 0x7f7f7f7f
int l,Test_num,mxc,mxx;
char ch[53];
int f[53][53][511][2];
inline int min(int a,int b)
{
return a<b? a:b;
}
inline int dp(int pos,int c,int x,int delta)
{
if(pos==l)return c? inf:0;if(c>mxc || c<-mxc || x>mxx || x<-mxx)return inf;
if(!(~f[pos][c+mxc][x+mxx][(delta==1)]))f[pos][c+mxc][x+mxx][(delta==1)]=min(dp(pos,c,x+delta,delta)+l-pos,min(dp(pos+1,c*10+ch[pos+1]-x,x,1),dp(pos+1,c*10+ch[pos+1]-x,x,-1)));
return f[pos][c+mxc][x+mxx][(delta==1)];
}
int main()
{
scanf("%s",ch+2),l=strlen(ch+2)+1,ch[1]=0,mxx=5*l,mxc=mxx/10+1;
for(int i=2;i<=l;++i)ch[i]^=48;
memset(f,-1,sizeof(f)),printf("%d",dp(0,0,0,1));
return 0;
}