Loading

买书

蒜头君去书店买书,他有 m 元钱,书店里面有 n 本书,每本书的价格为 pi 元。蒜头君很爱学习,想把身上钱都用来买书,并且刚好买 k 本书。请帮蒜头君计算他是否能刚好用 m 元买 k本书。

输入格式

第一行输入 33 个整数 m(1m100000000),n(1n30),k(1kmin(8,n))

接下来一行输入 nn 个整数,表示每本书的价格 pi(1pi100000000)。

输出格式

如果蒜头君能 刚好 用 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);
            }
        }//循环体可要可不要
    }
}

 同时, 在这里也是不需要标记的。思考:为什么?

 

posted @ 2018-02-10 12:38  机智的小白  阅读(192)  评论(0编辑  收藏  举报