1532. 找硬币
伊娃喜欢从整个宇宙中收集硬币。
有一天,她去了一家宇宙购物中心购物,结账时可以使用各种硬币付款。
但是,有一个特殊的付款要求:每张帐单,她只能使用恰好两个硬币来准确的支付消费金额。
给定她拥有的所有硬币的面额,请你帮她确定对于给定的金额,她是否可以找到两个硬币来支付。
输入格式
第一行包含两个整数
N
N
N 和
M
M
M,分别表示硬币数量以及需要支付的金额。
第二行包含 N N N个整数,表示每个硬币的面额。
输出格式
输出一行,包含两个整数
V
1
,
V
2
V_1,V_2
V1,V2,表示所选的两个硬币的面额,使得
V
1
≤
V
2
V_1≤V_2
V1≤V2 并且
V
1
+
V
2
=
M
V_1+V_2=M
V1+V2=M。
如果答案不唯一,则输出 V 1 V_1 V1 最小的解。
如果无解,则输出 No Solution
。
数据范围
1
≤
N
≤
105
,
1≤N≤105,
1≤N≤105,
1
≤
M
≤
1000
1≤M≤1000
1≤M≤1000
输入样例1:
8 15
1 2 8 7 2 4 11 15
输出样例1:
4 11
输入样例2:
7 14
1 8 7 2 4 11 15
输出样例2:
No Solution
思路:使用哈希表集合,Leetcode第一题两数之和的变种。(Set底层是Map)
用一个哈希表存储硬币。
对于每一枚硬币 x x x ,判断在集合中是否存在 y y y,使得 x + y = m x + y = m x+y=m。
如果存在,则是一组解,判断该解的小面值硬币是否小于 v 1 v_1 v1, 如果是,则存入 v 1 , v 2 v_1, v_2 v1,v2。
如果存在解,则输出。否则无解。
时间复杂度:一次循环,所以为 O ( n ) O(n) O(n) 。空间复杂度:开辟了一个堆,所以为 O ( n ) O(n) O(n)。
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int v1 = Integer.MAX_VALUE;//先给v1设置一个很大的数,超出题目的数据范围,方便后续对其更新
int v2 = -1;
Set<Integer> set = new HashSet<>();
for(int i = 0;i < n;i++){
int x = scanner.nextInt();
int y = m - x;
if(set.contains(y)){//找到了一组解
set.add(x);//将当前数加入集合set
//(x,y)是一组解,我们要找到里面较小的值去和v1比较,这里让x保存这组解中较小的数
if(x > y){
int temp = x;
x = y;
y = temp;
}
if(x < v1){//看看是否需要更新最终的解(v1,v2)
v1 = x;
v2 = y;
}
}else{
set.add(x);//将当前数加入集合set
}
}
if(v1 != Integer.MAX_VALUE){//v1被更新过,说明肯定找到了至少一组解
System.out.println(v1 + " "+ v2);
}else{
System.out.println("No Solution");
}
}
}
双指针
排序后
使用双指针。
左指针 l
指向数组头,右指针 r
指向数组尾。
如果 a[l] + a[r] > m
, 则 r
左移。
如果 a[l] + a[r] < m
, 则 l
右移。
如果 a[l] + a[r] = m
, 则找到解。
如果指针相遇还没有出现a[l] + a[r] = m
,则无解。
时间复杂度:排序
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),双指针
O
(
n
)
O(n)
O(n),总时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] arr = new int[n];
for(int i = 0;i < n ;i++){
arr[i] = scanner.nextInt();
}
//使用双指针前,基本都需要先对数组进行排序,即要求数组是有序的,之后才好根据情况移动左右指针
Arrays.sort(arr);
int l = 0,r = n - 1;
while(l < r){
if(arr[l] + arr[r] > m){
r--;
}else if(arr[l] + arr[r] < m){
l++;
}else{//首次找到一组arr[l]+arr[r] = m时,即是最终的的结果,因为arr是有序的,所以找到的第一组解就是v1最小的那组,可以break退出while循环了
break;
}
}
if(l < r){//退出上面的while循环后,还有l < r,说明是找到了解后break退出的
System.out.println(arr[l] + " " + arr[r]);
}else{
System.out.println("No Solution");
}
}
}