PAT-1048 Find Coins


Eva loves to collect coins from all over the universe, including some other planets like Mars. One day she visited a universal shopping mall which could accept all kinds of coins as payments. However, there was a special requirement of the payment: for each bill, she could only use exactly two coins to pay the exact amount. Since she has as many as 10​5​​ coins with her, she definitely needs your help. You are supposed to tell her, for any given amount of money, whether or not she can find two coins to pay for it.

Input Specification:

Each input file contains one test case. For each case, the first line contains 2 positive numbers: N (≤10​5​​, the total number of coins) and M (≤10​3​​, the amount of money Eva has to pay). The second line contains N face values of the coins, which are all positive numbers no more than 500. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print in one line the two face values V​1​​ and V​2​​ (separated by a space) such that V​1​​+V​2​​=M and V​1​​≤V​2​​. If such a solution is not unique, output the one with the smallest V​1​​. If there is no solution, output No Solution instead.

Sample Input 1:

8 15
1 2 8 7 2 4 11 15

Sample Output 1:

4 11

 

Sample Input 2:

7 14
1 8 7 2 4 11 15

Sample Output 2:

No Solution

解题思路 :

如果没有想到数据结构,直接暴力求解的话,也不是不可以,但是一定会超时。

先说说暴力求解怎么搞,按照题目题目要求vector保存coins面值,对vector排序,然后从第一个开始找是否存在一个硬币使得两个面值加起来等于总金额,通俗的说就是两个循环,第一层循环从前往后计算出剩余的面额,第二层循环从后往前但是不能超过上一层循环的下标,如果找到这个值,结束循环。

int size=data.size()
for(int i=0;i<size;i++){
		int last=total-data[i];
		for(int j=size-1;j>i;j--){
			if(data[j]==last){
				printf("%d %d",data[i],data[j]);
				return 0;
			}

		}
	}

 行得通是肯定的,但是一定会出现超时,太直白了。

下面我们按照这个思路来改进一下这个算法:

(1)添加硬币的时候,如果某个硬币的面值已经超过了总价钱,要么还要它干什么?保存数据的时候添加一个判断

while(num--){
		scanf("%d",&temp);
		if(temp<total){
		data.push_back(temp);
		}
	}

(2)从第一个硬币开始找(面值最小),这是无疑的,难道找第二个硬币的时候都要从最后一直找到第一层循环的下标在结束吗?当然不是的,试想一下,第二层循环中,如果从第j个硬币开始,两个硬币的面值之和已经小于总价钱,这时候我们往前搜还有意义吗?显示是没有的,因此加一个判断.

int size=data.size()-1;
for(int i=0;i<size;i++){
		int last=total-data[i];
		for(int j=size-1;j>i;j--){
			if(data[j]==last){
				printf("%d %d",data[i],data[j]);
				return 0;
			}
			else if(data[j]<last){
				break;
			}
		}
	}

(3)试想一下,如果没有找到与第一个硬币面值相匹配的硬币,第二个硬币还要从最后往前找吗?当然不,这里面会有数据重复。试想一下,vector数组从小到大排序,如果第一个硬币与下标为j之后的硬币面值之和大于总价钱,那么第二枚硬币与下标为j的硬币之后的硬币面值之和都会大于总价钱(因为第二个硬币面值比第一个硬币面值大)。所以我们设置一个下标变量来标志第二层循环下次开始的位置。初始值为data.size()-1

int size=data.size();
int lastindex=size-1;
for(int i=0;i<size;i++){
		int last=total-data[i];
		for(int j=lastindex;j>i;j--){
			if(data[j]==last){
				printf("%d %d",data[i],data[j]);
				return 0;
			}
			else if(data[j]<last){
				lastindex=j;
				break;
			}
		}
	}

 

All:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
	int num,total;
	scanf("%d %d",&num,&total);
	vector<int> data;
	int temp;
	while(num--){
		scanf("%d",&temp);
		if(temp<total){
		data.push_back(temp);
		}
	}
	sort(data.begin(),data.end());
	int size=data.size();
	int lastindex=size-1;
	for(int i=0;i<size;i++){
		int last=total-data[i];
		for(int j=lastindex;j>i;j--){
			if(data[j]==last){
				printf("%d %d",data[i],data[j]);
				return 0;
			}
			else if(data[j]<last){
				lastindex=j;
				break;
			}
		}
	}
	printf("No Solution");
	

	return 0;
}

大神的代码:有点散列的影子,一次循环即可完成
 

#include <cstdio>
#define L 501
int main() {
    int N, M;
    int cnt[L] = {0};
    scanf("%d %d", &N, &M);
    for(int i = 0, n; i < N; i++){
        scanf("%d", &n);
        cnt[n]++;
    }
    bool none = true;
    
    
    for(int k = 1, t; k < L && k <= M; k++){
        if(cnt[k] == 0) continue;
        t = M - k;
        if(t > 500){
            continue;
        }
        else if(t == k){
            if(cnt[k] >= 2){
                none = false;
                printf("%d %d", k, k);
                break;
            }
        }else{
            if(cnt[t] > 0){
                none = false;
                printf("%d %d", k, t);
                break;
            }
        }
    }
    
    if(none) printf("No Solution");
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值