题目链接->link
题意描述
给定n个面值的硬币和总数m,求两硬币之和等于m,其中v1<=v2,如果有多种解,那么输出v1最小的情况。
思路
- 此题可以用散列或者two pointers来做。
- 首先容易想到的做法是,现将硬币值从小到大排序,然后:
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(coin[i]+coin[j]==m)
printf("%d %d\n",coin[i],coin[j]);
flag=1;
break;
}
if(flag)break;
}
时间复杂度为O(n2),这样做会超时,并且当coin[i]+coin[j]>m时,因为递增序列,所以j+1及其后面所有序列都是不符合的。
- 改进方法就是设置i,j分别指向首尾,当i+j对应元素等于m那么输出,退出循环;当i+j元素小于m,那么i要右移一位;当i+j小于m,那么j要左移一位;当i==j时,循环结束。这样时间复杂度为O(n)。
- 散列法可以用int hashmap[]记录硬币对应面值的数量,那么只要判断hashmap[i]和hashmap[m-i]是否同时存在;并且如果有i==m-i的情况,要求数量>=2。
代码
two pointers解法
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100005;
int coin[maxn];
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d",&coin[i]);
}
sort(coin, coin+n);//将硬币从小到大排序
int i=0,j=n-1;//two pointers思想,i指向头,j指向尾
while(i<j){
if(coin[i]+coin[j]==m){//如果两元素相加等于m,就输出并退出循环
printf("%d %d\n",coin[i],coin[j]);
break;
}
if(coin[i]+coin[j]<m){//如果两者相加小于m,那么i应该右移
i++;
}
if(coin[i]+coin[j]>m){//如果两者相加大于m,那么j应该左移
j--;
}
}
if(i==j)printf("No Solution\n");//如果i==j,说明没有两元素相加等于m
return 0;
}