Educational Codeforces Round 104 (Rated for Div. 2) F.Ones(数位dp)

这篇博客介绍了一种动态规划的方法来解决给定一个正整数n,使用1和加减运算找到最少需要多少个1来表示n的问题。通过分析1的后效性和高位对低位的影响,博主提出了从高位到低位的动态规划状态转移方程,并给出了相应的C++代码实现。该算法考虑了可能的借位情况,并优化了1的使用次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

给一个正整数n(1<=n<=1e50),你只能用包含1的数以及加减两种运算去计算这个数,问最少需要多少个1。

例如,24=11+11+1+1,最少需要6个1;而102=111-11+1+1,最少需要7个1。

思路来源

libreOJ群

题解

一个很重要的性质是,注意到111111*6=1111111-111111*4-1,即用6次看上去不如从高位借下来优,

但是有1*6=11-1*5,所以表明每一个长度的串1都最多用不会超过6次

也就是低位的1不会产生进位,即不会对高位产生影响,

前对后有影响,后对前无影响,这启发我们从高到低dp,

dp[i][j][k]表示考虑前i位时,当前造的前缀这个数和原串前缀i位的数相差为j,当前还有k个前缀1在后续发挥作用时的最小需要的1的个数

其实就是需要存下来两个状态,一个是和真正的答案还偏离多少,

另一个是还有多少个1有后效性,记录下来就没有后效性了

考虑到可能要借位,前面补几个0

代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define dbg1(x) cerr<<#x<<"="<<(x)<<" "
#define dbg2(x) cerr<<#x<<"="<<(x)<<"\n"
const int A=200,B=330,INF=0x3f3f3f3f;
void Min(int &x,int y){x=min(x,y);}
int n,dp[55][A*2+5][B*2+5];
//111111*6=1111111-111111*4-1
//1*6=11-1*5
string s;
int main(){
	cin>>s;
	s="0000"+s;
	memset(dp,INF,sizeof dp);
	dp[0][A][B]=0;
	n=s.length();
	for(int i=0;i<n;++i){
		for(int j=-A;j<=A;++j){
			for(int k=-6*i;k<=6*i;++k){
				if(dp[i][j+A][k+B]==INF)continue;
				//dbg1(i),dbg1(j),dbg1(k),dbg2(dp[i][j+A][k+B]);
				for(int l=-6;l<=6;++l){
					int nk=k+l;
					int nj=j*10+nk-(s[i]-'0');
					if(nj<-A || nj>A)continue;
					Min(dp[i+1][nj+A][nk+B],dp[i][j+A][k+B]+(n-i)*abs(l));
				}
			}
		}
	}
	int ans=INF;
	for(int k=-6*n;k<=6*n;++k){
		ans=min(ans,dp[n][A][k+B]);
	}
	printf("%d\n",ans);
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值