1532. 找硬币
伊娃喜欢从整个宇宙中收集硬币。
有一天,她去了一家宇宙购物中心购物,结账时可以使用各种硬币付款。
但是,有一个特殊的付款要求:每张帐单,她只能使用恰好两个硬币来准确的支付消费金额。
给定她拥有的所有硬币的面额,请你帮她确定对于给定的金额,她是否可以找到两个硬币来支付。
输入格式
第一行包含两个整数 NNN 和 MMM,分别表示硬币数量以及需要支付的金额。
第二行包含 NNN个整数,表示每个硬币的面额。
输出格式
输出一行,包含两个整数 V1,V2V_1,V_2V1,V2,表示所选的两个硬币的面额,使得 V1≤V2V_1≤V_2V1≤V2 并且 V1+V2=MV_1+V_2=MV1+V2=M。
如果答案不唯一,则输出 V1V_1V1 最小的解。
如果无解,则输出 No Solution。
数据范围
1≤N≤105,1≤N≤105,1≤N≤105,
1≤M≤10001≤M≤10001≤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)
用一个哈希表存储硬币。
对于每一枚硬币 xxx ,判断在集合中是否存在 yyy,使得 x+y=mx + y = mx+y=m。
如果存在,则是一组解,判断该解的小面值硬币是否小于 v1v_1v1, 如果是,则存入 v1,v2v_1, v_2v1,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(nlogn)O(nlogn)O(nlogn),双指针O(n)O(n)O(n),总时间复杂度 O(nlogn)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");
}
}
}

这篇博客探讨了一个找硬币支付问题的算法,其中伊娃需要使用恰好两个硬币来支付账单。提出了两种解决方案:哈希表集合方法和双指针排序法。哈希表集合方法通过遍历硬币并检查是否存在与剩余金额相加等于账单的硬币,而双指针排序法则是对硬币进行排序后,使用两个指针从两端寻找和为目标金额的组合。两种方法的时间复杂度分别为O(n)和O(nlogn)。
451

被折叠的 条评论
为什么被折叠?



