AtCoder Beginner Contest 234 E - Arithmetic Number

2 篇文章 0 订阅
题目描述(传送门):

在这里插入图片描述
我们将满足以下条件的正整数n称为算术数(Arithmetic Number)。

  • di 表示n的第i位数(从左向右数,并且n用十进制表示,数字前没有多余的零),其满足(d2-d1)=(d3-d2)=…=(dk-dk-1),其中k是n的位数。
  • 如果n是一位数,则其被认为是算术数。

比如,234,369,86420,17,95,8,11,777都是算术数,而751,919,2022,246810,2356不是。
请找到一个最小的算术数,但其不能小于X。X是介于1到1017之间的数(包括1与1017

解题思路:

以下为当时的思路,存在绕路,可以直接看黄色高光的部分
可以将题目理解为:给定一个数X,找到一个不小于X的数Y,使Y的每一位组成的数列是等差数列。

最开始让我想到了柱状图
在这里插入图片描述
要怎么变化才能让其变成一个等差的数列呢?像是台阶一样,可以是上楼的,下楼的,也可以是平地。
我先是认为改变低位得到的数要比改变高位得到的数要小,所以要先改变低位的数,不行的话再改变高位的数,这样最后的结果才会尽可能地小。但是感觉这样太麻烦了,我还没找到关键。后来发现,不管低位怎么变化,最高位两个数d1,d2的差才真正决定了最后等差数列的。这样看来,左边两个数才是关键。那么可以对这两个数进行分类讨论,粗略地看,似乎有三种情况:① d1>d2、② d1=d2、③ d1<d2。在第①种情况下,如果顺着d1,d2的差形成的新数列的每一个数都大于等于旧数列对应位置的di,则该新数列即为结果。像是这样,其中绿色代表新数列,紫色代表旧数列。
在这里插入图片描述
如果是第②种情况,同样如果顺着d1,d2的差形成的新数列的每一个数都大于等于旧数列对应位置的di,则该新数列即为结果。
在这里插入图片描述
这样似乎和d1,d2谁大谁小没有关系,因为不管谁大,判断的方法都一样。不过可以发现,在d1,d2不变的情况下,形成的新数列如果满足条件,则该新数列即为结果,我们可以称其为第①种情况。如果不满足第①种情况,则需要增大d1,d2(如d1=1,d2=9,将其按两位数看待,则增大一位即为d1=2,d2=0),按新的d1,d2形成新数列,再判断新数列是否满足条件,我们可以称其为第②种情况。但不管是第①种情况还是第②种情况,在形成新数列的过程中都需要注意一点:按差形成数时,该数不能大于9或小于0,因为该数只是表示一位数

但是WA了,我又忽略了一点。

看这样一种情况,问形成的数列是否满足条件。
在这里插入图片描述
888888>884933,所以满足条件,而我的程序判了不满足条件。这是因为我下意识地认为旧数列的每一个数都应当小于等于新数列对应的数,但其实一个数列整体代表的是一个数,这种判断依据不符合实际的判断。但也不至于推翻重来,打个补丁就好了。在比较新、旧数列的过程中,如果新数列的某位数大于旧数列对应的数,则之后只需判断新数列的每位数是否在区间[0,9]内;若等于,则继续判断;若小于,可直接判定新数列不满足条件,不管后面的比较结果。进一步地,我又发现,如果改变了d1,d2,那只需判断新数列的每位数是否在区间[0,9]内即可。
所以简单地说,我们可以按d1,d2来形成新数列,如果新数列的每一位均在区间[0,9]内,则判断该数列表示的数是否大于等于旧数列表示的数,若是,则为结果;若否,则增大d1,d2(如d1=1,d2=9,将其按两位数看待,则增大一位即为d1=2,d2=0),继续按d1,d2来形成新数列,直到找到满足条件的数列。
以上是我当时的思路。下面是对应的代码

代码实现:
#include<iostream>
using namespace std;
int a[20],len;
bool judge() {//判断是否满足第①种情况
	int dif=a[1]-a[0],k=a[1];
	bool flag=false;
	for(int i=2; i<len; i++) {
		k+=dif;
		if(k>9||k<0||(k<a[i]&&!flag)) return false;
		if(k>a[i]) flag=true;
	}
	return true;
}
bool judge2() {//用于判断是否满足第②种情况
	int dif=a[1]-a[0],k=a[1];
	for(int i=2; i<len; i++) {
		k+=dif;
		if(k>9||k<0) return false;
	}
	return true;
}
int main() {
//	freopen("1.txt","r",stdin);
	string s;
	cin>>s;
	if(s.size()<=2) {
		cout<<s<<endl;
		return 0;
	}
	len=s.size();
	for(int i=0; i<s.size(); i++) {
		a[i]=s[i]-'0';
	}
	if(!judge()) {//如果不满足第①中情况
		for(int i=a[0]*10+a[1]+1; i<100; i++) {
			a[0]=i/10;//d1
			a[1]=i%10;//d2
			if(judge2()) break;
		}
	}
	int dif=a[1]-a[0];
	for(int i=2; i<len; i++) {//形成新数列
		a[i]=a[i-1]+dif;
	}
	for(int i=0; i<len; i++) cout<<a[i];
	cout<<endl;
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值