答题链接
https://www.nowcoder.com/practice/46eb436eb6564a62b9f972160e1699c9?tab=answerKey
题目描述
给出一个正整数N和长度L,找出一段长度大于等于L的连续非负整数,他们的和恰好为N。答案可能有多个,我我们需要找出长度最小的那个。
例如 N = 18 L = 2:
5 + 6 + 7 = 18
3 + 4 + 5 + 6 = 18
都是满足要求的,但是我们输出更短的 5 6 7
大佬的题解
思想:先根据等差数列的公式,求得满足条件等差数列和Sn=N时,等于a1与n的关系,a1表示满足关系的子数组的第一个元素,n表示该子数组的元素个数,根据题意,要求n最小,即数组元素个数最少嘛,且要大于L,那么我们就让n从L开始累加,直到100(题中给定),我们遍历到的一个满足Sn=N的n的值就是我们所要求的最小值,我们再根据这个n来求到a1(数组开始元素),然后打印a1后面的n-1个元素就可以了
疑问:为什么遍历到第一个满足条件的n就能保证得到的满足题意的子数组长度最小?
这确实是我刚看到这个题解所产生的疑问,而且,网上的题解大都没写原因,这是因为,我们是把n从给定的L开始遍历,越往后满足条件的n越大,那肯定得到的第一个满足条件的n是最小的呀。
import java.util.Scanner;
public class Main{
public static void main(String[] arg0){
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int L = sc.nextInt();
int n;
boolean flag = false;
for (n=L;n <= 100; n++){
if((2 * N - n * n + n) % (2 * n) == 0){
//表示当前n能确定某个a1满足条件,能除尽,没有余数
int start = (2 * N - n * n + n) / (2 * n); // (1)找a1
for (int i=0; i < n-1; i++)
System.out.print((start + i) + " ");//依次打印a1,a2...a1+n-2
System.out.print(start + n - 1); // (2)//本来可以在上面循环就打印,但是牛客要求最后不能有空格,所以单独打印
flag = true;
break;
}
}
if(!flag){
System.out.println("No");
}
}
}
注意点
1:也许有人疑问下面这句代码是为何,因为我们不知道a1的具体数值啊,只能用取余等于0表示当前的n能够找到某个a1开始的长度为n的子数组满足题意,再进行进一步的操作
if((2 * N - n * n + n) % (2 * n) == 0)
试想一下 A/B=C,说明A%B=0(能除尽,余数为0)
2:我有看到有些题解是在scanner用完之后会关闭输入流的
scanner.close()
3:上述题解中的flag标志位其实是可以不用的,把break换成return 即可
import java.util.Scanner;
public class Main{
public static void main(String[] arg0){
Scanner scanner=new Scanner(System.in);
int N=scanner.nextInt();
int L=scanner.nextInt();
scanner.close();
int n=0;
for( n=L;n<=100;n++)
{
if((2*N+n-n*n)%(2*n)==0 )
{
int start=(2*N+n-n*n)/(2*n);
for(int i=0;i<n-1;i++)
{
System.out.print((start+i)+" ");
}
System.out.println((start+n-1));
return ;//如果有满足题意的子数组,那么在打印完成之后,这里就返回,程序结束
}
}
System.out.println("No"); //n循环到100,没有找到合适的值,才会打印"No"
}
}
我的解题思路
我的思路是创建二维数组,利用动态规划的思想填写二维表格,不过我是按列填写的,找到dp[j][i]=N,如果此时的因子长度比前一个的因子长度小,就替换成当前得到的因子。.
大概的意思就是每一列的下一个值跟上一个值之间有一个固定的差值,然后如果我们从最后一列开始填的话,最新找到满足条件的就是最短的数组
对比一下,我的思路真的是太复杂了,还是要利用数学知识
代码
import java.util.*;
public class Main{
public static void main(String args[])
{
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine().toString();
int[] dis = getIntArr(str);
int N=dis[0];
int L=dis[1];
snippet(N,L);
}
static int[] getIntArr(String str){
String[] arr = str.split(" ");
int[] b = new int[arr.length];
for(int j = 0; j<b.length;j++) {
b[j] = Integer.parseInt(arr[j]);
}
return b;
}
static void snippet(int N,int L)
{
int size=N/L+L/2;//需要往两边扩展一下
int dp[][]=new int[size][size];
List<Integer>list=new ArrayList<>();
int length=size;//初始化就搞大一点
for(int i=0;i<size;i++)
{
dp[i][i]=i+1;
}
for(int i=0;i<size;i++) //至少两个数相加,那么这两个数不可能超过N的一半,三个数相加,就不可能超过1/3
{
for(int j=0;j<size;j++)
{
if(j+1+i<size)
{
dp[j+i+1][i]=dp[j+i][i]+j+2+i; //先填第一列,第二列的值可以参考第一列来填
if(dp[j+i+1][i]==N)
{
int temp=i+1;
if(j+2<length&&j+2>=L)
{
length=j+2;
list.clear();
for(int k=0;k<=j+1;k++)
{
list.add(temp++);
}
}
}
}
}
}
if(list.size()==0)
{
System.out.print("No");
}
else{
for(int p=0;p<list.size()-1;p++)
{
System.out.print(list.get(p)+" ");
}
System.out.print(list.get(list.size()-1));
}
}
}
哎!,不知道是什么错,也不知道怎么调
看了下测试结果,应该是没有问题的呀
总结:
还是要想到多利用数学的思想啊