【數論】B048_Jam's balance(暴搜 / dp / 打表 / 二進制枚舉)

一、Problem

Jim has a balance and N weights. (1≤N≤20) The balance can only tell whether things on different side are the same weight.

Weights can be put on left side or right side arbitrarily.

Please tell whether the balance can measure an object of weight M.

Input

The first line is a integer T(1≤T≤5), means T test cases. For each test case :

The first line is N, means the number of weights.

The second line are N number, i’th number wi(1≤wi≤100) means the i’th weight’s weight is wi.

The third line is a number M. M is the weight of the object being measured.

Output

You should output the “YES"or"NO”.

Sample Input
1				T
2				N
1 4			    int[] w
3				M
2
4
5

Sample Output
NO
YES
YES

Hint

For the Case 1:Put the 4 weight alone

For the Case 2:Put the 4 weight and 1 weight on both side

二、Solution

http://acm.hdu.edu.cn/showproblem.php?pid=5616

題意:有 N 個砝碼,和 M 個待測物體,假設物品開始放在左盤,砝碼可以放於天平左右。問是否能用砝碼測出待測物體的重量…

方法一:暴搜(超時)

  • 設當前重量選擇總砝碼爲 cur,對於每一種砝碼 b[i],我們都可以做出三種選擇:
    • 放在天平右邊,即:cur+b[i]
    • 放在天平左邊,即:cur-b[i]
    • 跳過,即:cur
  • 如果狀態 W 可達,輸出 YES
  • 否則輸出 NO

留下 TLE 的淚水…😂

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static class Solution {
		int[] b;
		int W, N;
		int MAXN = 2000 + 50;
		boolean done;
		
		void dfs(int d, int cur) {
			if (d > N || done)
				return;
			if (cur == W) {
				System.out.println("YES");
				done = true;
				return;
			}
			dfs(d+1, cur+b[d]);
			dfs(d+1, cur-b[d]);
			dfs(d+1, cur);
		}
		void init() {
			Scanner sc = new Scanner(new BufferedInputStream(System.in));
			int T = sc.nextInt();
			while (T-- > 0) {
				N = sc.nextInt();
				b = new int[MAXN];
				for (int i = 0; i < N; i++) {
					b[i] = sc.nextInt();
				}
				int m = sc.nextInt();
				for (int i = 0; i < m; i++) {
					W = sc.nextInt();
					dfs(0, 0);
					if (!done)
					    System.out.println("NO");
					done = false;
				}
			}
		}
	}
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
		s.init();
    }
}

複雜度分析

  • 時間複雜度:O(3N)O(3^N)
  • 空間複雜度:O(...)O(...)

方法二:01 dp

  • 定義狀態
    • dp[x]=truedp[x] = true,表示瑪法組合的重量爲 x 可達…
  • 思考狀態轉移方程
    • dp[j]=dp[j]  dp[jw[i]]dp[j] = dp[j]\ |\ dp[j-w[i]] 表示當前總重量 j 是否能表示取決於 dp[]
  • 思考初始化:
    • dp[]
  • 思考輸出:dp[]
  • 考慮狀態壓縮:dp[]

複雜度分析

  • 時間複雜度:O()O()
  • 空間複雜度:O()O()

方法三:打表

整體思路:

  • 數組 int[] b 保存每個砝碼的重量以及重量的相反數,爲什麼呢?
  • 如果在 b 中取到重量 w 的負數,相當於把砝碼 w 放在了天平的左邊;右邊同理…
  • 問題轉化爲:在數組 b 中取出一些數,看是否能組成題目詢問的物品的重量…
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static class Solution {
		void init() {
			Scanner sc = new Scanner(new BufferedInputStream(System.in));
			int T = sc.nextInt();
			while (T-- > 0) {
				int N = sc.nextInt();
				int[] b = new int[2*N+5];
				int tot = 0;
				for (int i = 0; i < N; i++) {
					b[tot++] = sc.nextInt();
					b[tot] = -b[tot-1];
					tot++;
				}
				boolean[] can = new boolean[2000];
				List<Integer> all = new ArrayList<>();
				all.add(0); can[0] = true;
				
				for (int i = 0; i < tot; i++) {
					int size = all.size();
					for (int j = 0; j < size; j++) {
						int n = all.get(j) + b[i];
						if (n > 0 && !can[n]) {
							can[n] = true;
							all.add(n);
						}
					}
				}
				int m = sc.nextInt();
				for (int i = 0; i < m; i++) {
					int w = sc.nextInt();
					System.out.println(can[w] ? "YES" : "NO");
				}
			}
		}
	}
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
		s.init();
    }
}

複雜度分析

  • 時間複雜度:O(n2)O(n^2)
  • 空間複雜度:O(n)O(n)

方法四:二進制枚舉

  • 由前面的打表法可以發現其實就是一種枚舉砝碼重量的子集的方法,這裏可用二進制枚舉代替打表。
  • 比如在集合 [1,2,3,4][1,2,3,4] 中選擇 1 和 4,相當於用二進制位表示爲 0b1001。
  • 但是要注意,我們一定還需要 4-1,4-2 …,這樣的組合,所以在枚舉的時候,需要再減去某些砝碼的重量…
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static class Solution {
		void init() {
			Scanner sc = new Scanner(new BufferedInputStream(System.in));
			int MAXN = 2000 + 50;
			int T = sc.nextInt();
			while (T-- > 0) {
				int N = sc.nextInt();
				int[] b = new int[MAXN];
				for (int i = 0; i < N; i++) {
					b[i] = sc.nextInt();
				}
				boolean[] can = new boolean[2*MAXN];
				int tot = 1 << N;
				for (int i = 0; i < tot; i++) {
					int sum = 0;
					for (int j = 0; j < N; j++) {
						if ((i & (1 << j)) > 0)
							sum += b[j];
					}
					can[sum] = true;
					for (int k = 0; k < N; k++) {
						int n = sum - b[k];
						if (n >= 0 && !can[n])
							can[n] = true;
					}
				}
				int m = sc.nextInt();
				for (int i = 0; i < m; i++) {
					int w = sc.nextInt();
					System.out.println(can[w] ? "YES" : "NO");
				}
			}
		}
	}
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
		s.init();
    }
}

複雜度分析

  • 時間複雜度:O()O()
  • 空間複雜度:O()O()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章