题解 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;
}
posted @ 2021-03-13 11:48  18Michael  阅读(79)  评论(0编辑  收藏  举报