买书
蒜头君去书店买书,他有 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);
}
}//循环体可要可不要
}
}
同时, 在这里也是不需要标记的。思考:为什么?

浙公网安备 33010602011771号