AC1.8(0.1+1.0+0.7),很菜,第三次笔试。
前两次笔试(百度2019/04/02,拼多多2019/04/03)的时候,有些题目看完连反应都没有,智商被按在地上摩擦,心态大崩。
痛定思痛,刷了两天算法题,算是有点小小进步,至少今晚三道题目都有点感觉。
记录一下,如果有人讨论一下就更好了。
1.(AC0.1)给定n,m,是否能够利用n种不同面值的硬币组成1到m总值,如果可以输出最少需要几个硬币,不可以输出-1。
例如:n=4,m=20,n种面值硬币:1,2,5,10 输出5
笔试时想法:
两层嵌套循环,maxCount来统计至少需要多少数量的硬币;从钱总值a 从1到m;数量c来统计钱总值a至少需要多少硬币;硬币的面值b从大到小进行遍历,c=c+a/b,a=a%b,循环完判断一下a是否为0,是则继续循环,否则返回-1;如果c>maxCount,将maxCount=c。
漏洞:比如19=10*1+5*1+2*2(count=4)18=10*1+5*1+2*1+1*1(count=4),但是以19的面值组成成分没法组成18,dead。
现在想法(刚考完):(目前还未发现有啥漏洞)
两层嵌套循环,map1来统计各个面值硬币一共需要多少个;从钱总值a 从1到m;map2来统计钱总值a,各个面值硬币需要多少个;硬币的面值b 从大到小进行遍历,map的面值b中放入a/b,a=a%b;更新map1,如果map2某个面值需要的数量大于map1则更新,map1为大的那个值,并且统计一下map1中全部加起来的总值sum,在后续遍历中,如果钱总值a小于sum则直接跳过,不遍历。最后,统计一下map1中所有面值需要的数量总和,输出。
------------------------------------------------------2019/04/07更新---------------------------------------------------------
其实上面不用使用map,可以直接用数组来记录,更新了一下代码。
并为上面思路画了一个草图(一部分),方便大家理解,使用的栗子:以n=4,m=20,n种面值硬币:1,2,5,10 输出5
代码:
import java.util.Arrays;
import java.util.Scanner;
public class Coin {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int m = in.nextInt();
int n = in.nextInt();
int[] ns = new int[n];
for(int i=0;i<n;i++) ns[i] = in.nextInt();
Arrays.sort(ns);//排序硬币
/*
最小面值大于1,直接返回-1,因为1首先就没法组合出来,
其实只要有面值为1的硬币,什么都可以组合出来
*/
if(ns[0]>1 ) {
System.out.println(-1);
return;
}
long[] result = new long[n];//result记录总共各面值需要的数量
int sum = 0;//result所有面值加起来的总和sum
for(int i=1;i<=m;i++) {
if(i<=sum) continue;//钱总和 i 小于sum,就直接跳过
int money = i;//当前要组成钱总值
for(int j=n-1;j>=0;j--) {
if(money/ns[j] != 0) {
//当需要第j枚硬币的数量大于result第j枚硬币数量,则更新result中数量
if(money/ns[j] > result[j]) result[j] = money/ns[j];
money = money%ns[j];
}
if(money == 0) break;
}
//计算新的sum
sum = 0;
for(int k=0;k<n;k++) {
sum += result[k]*ns[k];
}
}
//统计总共需要多少个硬币
int count = 0;
for (long c : result) count += c;
System.out.println(count);
}
}
2.(AC1.0)给定一个字符串,只有1和0,如果相邻的两个字符分别为1和0,则可以抵消,一直执行下去,问字符串最后剩下几个字符。(送分题)
想法:
统计字符串中0的数量A和1的数量B,返回A-B的绝对值。
(太简单了,就不贴代码了)
3.(AC0.7)你要穿过山谷,前面有n只怪兽,怪兽i你可以花费pi来收买它,它会护送你穿过山谷,如果遇到一个怪兽j,它的战斗力是dj,护送你的全部怪兽加起来的战斗力大于dj,你不需要收买它,否则你需要收买它才能通过。问你最少需要多少钱来穿过山谷。
如,输入n=4,d[]={1,2,4,8} p={1,2,1,2} 输出 6
输入n=3,d[]={8,5,10} p={1,1,2} 输出2
笔试时想法:
变量maxIndex来表示最大怪兽的下标,变量left表示要战胜最大怪兽的战斗值,怪兽从后往前遍历,如果碰到怪兽的武力值大于left就更新maxIndex和left;收不收买当前的怪兽i取决于怪兽i之前的怪兽的战斗力加起来是否大于d[maxIndex],大于等于则当前怪兽就可以不收买,小于收买,left=left-怪兽i武力值。
漏洞:好像没有考虑到收买的钱,只考虑能不能打败怪兽(●—●),具体怎么改还没有想法
------------------------------------------------------2019/04/07更新---------------------------------------------------------
看了牛客网很多帖子,正解应该是深度搜索+剪枝,但是题目的n是10^12,所以解空间很大,会花费很多时间。这里贴一个牛客网大佬AC的代码,思路就是当怪兽i通过不了的时候就比较两个条件①收买怪兽i的钱+当前已经花费的钱是否大于收买怪兽i之前全部怪兽的钱②怪兽i之前的全部怪兽的武力值总和是否大于怪兽i的武力值,两个条件为true则收买怪兽i之前全部怪兽,否则收买怪兽i。
注明出处:
作者:永航 链接:https://www.nowcoder.com/discuss/173782?type=0&order=0&pos=6&page=1
代码:
import java.util.Scanner;
public class Third {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long[] powers = new long[n];
long[] moneys = new long[n];
for (int i = 0; i < n; i++) {
powers[i] = sc.nextLong();
}
for (int i = 0; i < n; i++) {
moneys[i] = sc.nextLong();
}
long currPower = 0;
long totalMoney = 0;
for (int i = 0; i < n; i++) {
if (currPower < powers[i]){
if ((totalMoney+moneys[i]) > sum(moneys,0,i-1) &&powers[i] < sum(powers,0,i-1)){
totalMoney = sum(moneys,0,i-1);
currPower = sum(powers,0,i-1);
continue;
}
totalMoney += moneys[i];
currPower += powers[i];
}else{
continue;
}
}
System.out.println(totalMoney);
}
private static long sum(long[] arr, int start, int end){
long sum = 0;
for (int i = start; i <= end; i++){
sum += arr[i];
}
return sum;
}
2019/04/06 0:17 记录一下笔试时候的想法,希望后续通过自己或社区可以得到这三道题的AC1.0的解法。
2019/04/08 0:26 更新了第一题帮助理解的草图,第三题一个AC的代码