设计算法就是一个不断降低时间复杂度的过程——《挑战程序设计》

在这里插入图片描述
这道题是第一章开篇的第一道例题,但也是第一章的最后一道例题,改变的只是数据范围。首先,作为第一道算法竞赛的练手题,直接上4个for循环判断是否相等就完事了,复杂度为 n 4 n^4 n4,但n<50的数据范围完全可以接受。

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt(),m=sc.nextInt();
		int[] arr=new int[n];
		for(int i=0;i<n;i++) {
			arr[i]=sc.nextInt();
		}
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				for(int k=0;k<n;k++) {
					for(int z=0;z<n;z++) {
						if(arr[i]+arr[j]+arr[k]+arr[z]==m) {
							System.out.println("Yes");
							return;
						}
					}
				}
			}
		}
		System.out.println("No");
	}
}

但作为一名算法竞赛的选手,或者哪怕只是一个程序员,我们也都应该去寻找一些更优质的解法,也就是把时间复杂度给降低。首先,我们最容易想的是最里面的循环,这个循环判断arr[i]+arr[j]+arr[k]+arr[z]是否等于m,不如我们稍微把这个等式变形,arr[z]=m-arr[i]+arr[j]+arr[k],这样做的目的是什么?目的就是这样我们可以在数组里寻找是否存在m-arr[i]+arr[j]+arr[k]这么大的数字,通过预处理排序,我们可以用二分查找来完成这项任务,二分查找的复杂度是logn,明显要比n快很多,而排序是在循环之前就处理好的,所以是 n l o g n nlogn nlogn,而后面三个循环+一个二分查找,复杂度为 n 3 l o g n n^3logn n3logn,整体复杂度也是 n 3 l o g n n^3logn n3logn。另外,像java或者c++这种比较常见的语言中自带了二分查找的函数。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt(),m=sc.nextInt();
		int[] arr=new int[n];
		for(int i=0;i<n;i++) {
			arr[i]=sc.nextInt();
		}
		Arrays.sort(arr);
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				for(int k=0;k<n;k++) {
					if(Arrays.binarySearch(arr, m-arr[i]-arr[j]-arr[k])>=0) {
						System.out.println("Yes");
						return;
					}
				}
			}
		}
		System.out.println("No");
	}
}

可是这样就够了吗?这本书预期的要求是这个算法能在1秒内处理n小于1000范围内的数字,这样明显还是会超时。所以,我们这次着手两个循环来看,同样的如果我们可以把等式写成m-arr[i]-arr[j],然后再进行二分查找,但与一开始不同,我们不能直接得出arr[k]+arr[z]的值,所以我们需要预处理,用一个n*n大的一维的数组,预先存储arr[k]+arr[z]的值,然后进行排序,这样我们就可以二分查找这个数组来进行判断了。另外,这个数组中会存在一部分重复的元素,但对结果造成的影响不大,所以可以不用理会。

import java.util.Arrays;
import java.util.Scanner;

public class 抽签问题高级优化 {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt(),m=sc.nextInt();
		int[] arr=new int[n];
		int[] arr1=new int[n*n];
		for(int i=0;i<n;i++) {
			arr[i]=sc.nextInt();
		}
		int p=0;
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				arr1[p++]=arr[i]+arr[j];
			}
		}
		Arrays.sort(arr1);
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
					if(Arrays.binarySearch(arr1, m-arr[i]-arr[j])>=0) {
						System.out.println("Yes");
						return;
					}
			}
		}
		System.out.println("No");
	}
}

这样,我们的复杂度降到了 n 2 l o g n n^2logn n2logn,终于满足了n<1000的要求。其实很多时候,我们不一定能一开始就能直接想到最优算法,不妨先写出比较笨的方法,再基于这种方法慢慢降低它的时间复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值