蒜头君去书店买书,他有 m 元钱,书店里面有 n 本书,每本书的价格为 pi 元。蒜头君很爱学习,想把身上钱都用来买书,并且刚好买 k 本书。请帮蒜头君计算他是否能刚好用 m 元买 k本书。
输入格式
第一行输入 33 个整数 m(1≤m≤100000000),n(1≤n≤30),k(1≤k≤min(8,n))
接下来一行输入 nn 个整数,表示每本书的价格 pi(1≤pi≤100000000)。
输出格式
如果蒜头君能 刚好 用 m 元买 k 本书,输入一行"Yes"
, 否则输出"No"
。
样例输入1
10 4 4 1 2 3 4
样例输出1
Yes
样例输入2
10 4 3 1 2 3 4
样例输出2
No
-------------------------------------------------------------------------------------------------------------------------------------------------------------
第一种思路:二进制枚举法
//二进制枚举,会超时,不会错 import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Scanner; public class Main { static int[] book; public static void main(String[] args) { Scanner sc = new Scanner(System.in); int m = sc.nextInt(); int n = sc.nextInt(); int k = sc.nextInt(); int ans = 0; book = new int[n]; for(int i = 0; i < n; i ++) { book[i] = sc.nextInt(); } for(int i = 0; i < (1 << n); i ++) { int x = 0; List<Integer> list = new ArrayList<Integer>(); for(int j = 0; j < n; j ++) { if((i & (1 << j)) != 0) { x ++; list.add(j); } } int sum = 0; Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()) { sum += book[book.length - 1 - iterator.next()]; } if(x == k && sum == m) ans ++; } if(ans != 0) System.out.println("Yes"); else System.out.println("No"); } }
第二种思路:随机数骗分法
//随机数骗分 public class Main { static int[] book; public static void main(String[] args) { Scanner sc = new Scanner(System.in); int m = sc.nextInt(); int n = sc.nextInt(); int k = sc.nextInt(); book = new int[n]; for(int i = 0; i < n; i ++) { book[i] = sc.nextInt(); } for(int i = 0; i < 10000000; i ++) { //数字千万不能设置太大,此处设置1000W即所有都超时 // normal版本:不能增加太多次数 /** * 100次,过了6组 * 1000次,过了6组 * 1W次,过了7组 * 10W次,过了8组 * 100W次,过了9组 * 可惜1000W次有超时,从OJ数据判断,第10组样例已经通过,得到正确的后break了,但前面No的有超时 * ☆☆☆☆☆200W次成功通过! */ Set<Integer> set = new HashSet<>(); for(int j = 0; j < k; j ++) { int rand = (int) (Math.random() * n); set.add(rand); //将随机产生的book数组的序数给放进去 } if(set.size() == k) { Iterator<Integer> iterator = set.iterator(); int sum = 0; while(iterator.hasNext()) sum += (book[iterator.next()]); if(sum == m) { System.out.println("Yes"); System.exit(0); } } // 理想的better版本:时间和空间占用少,上版Set有多余重复,可以增加次数 int a = (int) (Math.random() * n); int b = (int) (Math.random() * n); int c = (int) (Math.random() * n); int d = (int) (Math.random() * n); if(a != b && a != c && a != d && b != c && b != d && c != d) { int sum = book[a] + book[b] + book[c] + book[d]; if(sum == m) { System.out.println("Yes"); System.exit(0); } } //实际使用中,前者更好???为什么,因为一旦Yes,程序就exit了 } System.out.println("No"); } }
第三种思路:搜索算法
//超时 public class Main { static int[] book; static int[] mark; static int n; static int m; static int k; static int ans; public static void main(String[] args) { Scanner sc = new Scanner(System.in); m = sc.nextInt(); n = sc.nextInt(); k = sc.nextInt(); ans = 0; book = new int[n]; for(int i = 0; i < n; i ++) { book[i] = sc.nextInt(); } mark = new int[n]; dfs(0, 0, 0); if(ans != 0) System.out.println("Yes"); else System.out.println("No"); } private static void dfs(int step, int count, int price) { if(step == n && count == k && price == m) { ans ++; return; } if(step == n) { return; } for(int i = 0; i < 2; i ++) { //i = 0不要, i = 1要 if(mark[step] != 1) { if(i == 0) { mark[step] = 1; dfs(step + 1, count, price); mark[step] = 0; } else { mark[step] = 1; dfs(step + 1, count + 1, price + book[step]); mark[step] = 0; } } } } }
优化:当count > k的时候就可以return, 从而减少递归层数
import java.util.Scanner; public class Main { static int[] book;static int n; static int m; static int k; static int ans; public static void main(String[] args) { Scanner sc = new Scanner(System.in); m = sc.nextInt(); n = sc.nextInt(); k = sc.nextInt(); ans = 0; book = new int[n]; for(int i = 0; i < n; i ++) { book[i] = sc.nextInt(); } mark = new int[n]; dfs(0, 0, 0); if(ans != 0) System.out.println("Yes"); else System.out.println("No"); } private static void dfs(int step, int sum, int num) { if(step > n || sum > m || num > k) { return; } if(num == k) { if(sum == m) { ans ++; return; } else { return; } } if(step == n) return; for(int i = 0; i < 2; i ++) { //i = 0不要, i = 1要 if(i == 0) { dfs(step + 1, sum, num); } else { dfs(step + 1, sum + book[step], num + 1); } }//循环体可要可不要 } }
同时, 在这里也是不需要标记的。思考:为什么?