2017-09-25 15 views
7

Tôi đang cố giải quyết vấn đề sau:Số nguyên n bit nhỏ nhất c có k 1-bit và là tổng của hai số nguyên n bit có g, bit h được đặt thành 1 (lập trình động)

Tìm nhỏ nhất n-bit số nguyên c có k 1-bit và là tổng của hai số nguyên n-bit có g, h bit thiết lập để 1. g, h, k < = n

Để bắt đầu, số nguyên n bit ở đây có nghĩa là chúng tôi có thể sử dụng tất cả các bit n, nghĩa là CPC giá trị của một số nguyên như vậy là 2^n - 1. Số nguyên được mô tả có thể không tồn tại. Rõ ràng là trường hợp k > g + h không có giải pháp và cho g + h = k câu trả lời chỉ là 2^k - 1 (đầu tiên k bit là 1 bit, k - n số không ở phía trước).

Đối với một số ví dụ về những gì chương trình có nghĩa vụ phải làm:

g = h = k = 4, n = 10 : 
0000001111 + 0000001111 = 0000011110 
15 + 15 = 30 (30 should be the output) 


(4, 6, 5, 10): 
0000011110 + 0000111111 = 0001011101 
30 + 63 = 93 

(30, 1, 1, 31): 
1 + (2^30 - 1) = 2^30 

Như tôi đã nghĩ về nó, đây là một vấn đề lập trình năng động và tôi đã chọn phương pháp sau đây: Hãy dp[g][h][k][n][c] là số nguyên được mô tả và c là một bit tùy chọn để thực hiện. Tôi cố gắng tái tạo lại các khoản tiền có thể tùy thuộc vào các bit đặt hàng thấp nhất. Vì vậy, dp[g][h][k][n + 1][0] là tối thiểu

(0, 0):  dp[g][h][k][n][0] 
(0, 0): 2^n + dp[g][h][k - 1][n][1] 
(1, 0): 2^n + dp[g - 1][h][k - 1][n][0] 
(0, 1): 2^n + dp[g][h - 1][k - 1][n][0] 

Tương tự, dp[g][h][k][n + 1][1] là tối thiểu

(1, 1): dp[g - 1][h - 1][k][n][0] 
(1, 1): dp[g - 1][h - 1][k - 1][n][1] + 2^n 
(1, 0): dp[g - 1][h][k][n][1] 
(0, 1): dp[g][h - 1][k][n][1] 

Ý tưởng không phải là khó khăn nhưng tôi không thực sự có kinh nghiệm với những việc như vậy và thuật toán của tôi doesn 't làm việc ngay cả đối với trường hợp đơn giản nhất. Tôi đã chọn cách tiếp cận từ trên xuống. Thật khó cho tôi để xem xét tất cả các trường hợp góc. Tôi không thực sự biết nếu tôi đã chọn đúng cơ sở đệ quy, vv Thuật toán của tôi thậm chí không làm việc cho trường hợp cơ bản nhất cho g = h = k = 1, n = 2 (câu trả lời là 01 + 01 = 10). Không nên có câu trả lời cho g = h = k = 1, n = 1 nhưng thuật toán cung cấp cho 1 (về cơ bản là lý do tại sao ví dụ trước đây xuất kết quả là 1 thay vì 2). Vì vậy, ở đây có mã khủng khiếp của tôi (chỉ rất cơ bản C++):

int solve(int g, int h, int k, int n, int c = 0) { 
    if (n <= 0) { 
    return 0; 
    } 
    if (dp[g][h][k][n][c]) { 
    return dp[g][h][k][n][c]; 
    } 
    if (!c) { 
    if (g + h == k) { 
     return dp[g][h][k][n][c] = (1 << k) - 1; 
    } 
    int min, a1, a2, a3, a4; 
    min = a1 = a2 = a3 = a4 = std::numeric_limits<int>::max(); 
    if (g + h > k && k <= n - 1) { 
     a1 = solve(g, h, k, n - 1, 0); 
    } 
    if (g + h >= k - 1 && k - 1 <= n - 1) { 
     a2 = (1 << (n - 1)) + solve(g, h, k - 1, n - 1, 1); 
    } 
    if (g - 1 + h >= k - 1 && k - 1 <= n - 1) { 
     a3 = (1 << (n - 1)) + solve(g - 1, h, k - 1, n - 1, 0); 
    } 
    if (g + h - 1 >= k - 1 && k - 1 <= n - 1) { 
     a4 = (1 << (n - 1)) + solve(g, h - 1, k - 1, n - 1, 0); 
    } 
    min = std::min({a1, a2, a3, a4}); 
    return dp[g][h][k][n][c] = min; 
    } else { 
    int min, a1, a2, a3, a4; 
    min = a1 = a2 = a3 = a4 = std::numeric_limits<int>::max(); 
    if (g - 2 + h >= k && k <= n - 1) { 
     a1 = solve(g - 1, h - 1, k, n - 1, 0); 
    } 
    if (g - 2 + h >= k - 1 && k - 1 <= n - 1) { 
     a2 = (1 << (n - 1)) + solve(g - 1, h - 1, k - 1, n - 1, 1); 
    } 
    if (g - 1 + h >= k && k <= n - 1) { 
     a3 = solve(g - 1, h, k, n - 1, 1); 
    } 
    if (g - 1 + h >= k && k <= n - 1) { 
     a4 = solve(g, h - 1, k, n - 1, 1); 
    } 
    min = std::min({a1, a2, a3, a4}); 
    return dp[g][h][k][n][c] = min; 
    } 
} 
+4

Câu hỏi hay. Nhưng tôi tin rằng bạn nhảy quá nhanh đến kết luận sai (sử dụng lập trình động). Tôi sẽ tìm cách tiếp cận mang tính xây dựng hơn. Bằng cách giải quyết một vài trường hợp, như 1 <= k, g, h <= 3, bằng tay, bạn có thể nhận thấy một mẫu. Nitpick: "giá trị tối đa của một số nguyên như vậy là' 2^(n + 1) - 1' "- thực ra là' 2^n - 1'. – Gassa

+0

@Gassa. Nếu bạn đang gợi ý rằng đối với g + h = k + 1 thì giải pháp là 10 + k, cho g + h = k + 2 nó là 110 + k và vân vân, bạn sai. Thứ nhất, có thêm một chút mẫu cho g + h - k> k. Thứ hai, cả hai đều có counterexamples! – Atin

+1

Xin lỗi, tôi không có một mô hình cụ thể trong tâm trí. Thay vào đó, một phương pháp điều tra chung mà sẽ tiết lộ các mô hình - hoặc không tồn tại của nó, nếu đó thực sự là trường hợp. – Gassa

Trả lời

4

Bạn có thể tạo số tiền nhỏ nhất dựa trên số lượng bit g, h và k, mà không thực hiện bất kỳ chương trình động nào cả.Giả sử g ≥ h (chuyển đổi chúng bằng cách khác) đây là những quy tắc:

k ≤ h ≤ g

 11111111 <- g ones 
    111100000111 <- h-k ones + g-k zeros + k ones 
1000000000110 <- n must be at least h+g-k+1 

h ≤ k ≤ g

1111111111 <- g ones 
     11111100 <- h ones + k-h zeros 
    1011111011 <- n must be at least g+1 

h ≤ g ≤ k

1111111100000 <- g ones + k-g ones 
1100000011111 <- g+h-k ones, k-h zeros, k-g ones 
11011111111111 <- n must be at least k+1, or k if g+h=k 

Ví dụ: tất cả các giá trị của k cho n = 10, g = 6 và h = 4:

k=1   k=2   k=3   k=4  
0000111111 0000111111 0000111111 0000111111 
0111000001 0011000011 0001000111 0000001111 
---------- ---------- ---------- ---------- 
1000000000 0100000010 0010000110 0001001110 
k=4   k=5   k=6 
0000111111 0000111111 0000111111 
0000001111 0000011110 0000111100 
---------- ---------- ---------- 
0001001110 0001011101 0001111011 
k=6   k=7   k=8   k=9   k=10 
0000111111 0001111110 0011111100 0111111000 1111110000 
0000111100 0001110001 0011000011 0100000111 0000001111 
---------- ---------- ---------- ---------- ---------- 
0001111011 0011101111 0110111111 1011111111 1111111111 

Hoặc, đi thẳng với giá trị của c mà không tính a và b trước tiên:

k ≤ h ≤ g

c = (1 << (g + h - k)) + ((1 << k) - 2) 

h ≤ k ≤ g

c = (1 << g) + ((1 << k) - 1) - (1 << (k - h)) 

h ≤ g ≤ k

c = ((1 << (k + 1)) - 1) - (1 << ((g - h) + 2 * (k - g))) 

h + g = k

c = (1 << k) - 1 

mà kết quả trong mã đáng thất vọng trần tục này:

int smallest_sum(unsigned n, unsigned g, unsigned h, unsigned k) { 
    if (g < h) {unsigned swap = g; g = h; h = swap;} 
    if (k == 0) return (g > 0 || h > 0 || n < 1) ? -1 : 0; 
    if (h == 0) return (g != k || n < k) ? -1 : (1 << k) - 1; 
    if (k <= h) return (n <= h + g - k) ? -1 : (1 << (g + h - k)) + ((1 << k) - 2); 
    if (k <= g) return (n <= g) ? -1 : (1 << g) + ((1 << k) - 1) - (1 << (k - h)); 
    if (k < g + h) return (n <= k) ? -1 : (1 << (k + 1)) - 1 - (1 << (2 * k - g - h)); 
    if (k == g + h) return (n < k) ? -1 : (1 << k) - 1; 
    return -1; 
} 

Một số ví dụ kết quả:

n=31, g=15, h=25, k=10 -> 1,073,742,846 (1000000000000000000001111111110) 
n=31, g=15, h=25, k=20 ->  34,602,975 (0000010000011111111111111011111) 
n=31, g=15, h=25, k=30 -> 2,146,435,071 (1111111111011111111111111111111) 

(Tôi đã so sánh kết quả với của thuật toán brute-force cho mọi giá trị n, g, h và k từ 0 đến 20 để che ck chính xác, và không tìm thấy sự khác biệt.)

2

Tôi không quá thuyết phục về phương pháp lập trình động. Nếu tôi hiểu chính xác, bạn cần phải xác định cách truy cập dp[g + 1][h][k][n], dp[g][h + 1][k][n], dp[g][h][k + 1][n]dp[g][h][k][n + 1], có và không có bit mang theo, trong tính toán trước đó và tôi không chắc chắn về các quy tắc phù hợp cho tất cả những, cái đó.

Tôi nghĩ một cách dễ dàng hơn để suy nghĩ về vấn đề là cây A* search, trong đó mỗi nút chứa hai số ứng cử viên một phần để thêm, hãy gọi chúng là G và H. Bạn bắt đầu bằng nút có G = 0 và H = 0 ở mức m = 0, và làm việc như sau:

  1. Nếu G + H có n hoặc ít bit và k 1 bit, đó là giải pháp, bạn tìm thấy nó!
  2. Nếu không, nếu
    n - m < số lượng 1 bit trong G + H - k
    loại bỏ nút (không có giải pháp có thể).
  3. Ngược lại, nếu
    (g + h) - (số 1 bit trong G + số 1 bit trong H) < k - số 1 bit trong G + H
    loại bỏ các nút (không ứng cử viên sáng) .
  4. Nếu không, hãy phân nhánh nút thành cấp độ mới. Nói chung, bạn tạo tối đa bốn nút con của mỗi nút, trước tiên là G và H với 0 và 0, 0 và 1, 1 và 0 hoặc 1 và 1 tương ứng. Tuy nhiên:
    • Bạn chỉ có thể đứng trước G bằng 1 nếu số lượng 1 bit trong G nhỏ hơn g và tương tự cho H và h.
    • Ở cấp m (G và H có m bit), bạn chỉ có thể đứng trước G với 0 nếu
      n - m> g - số 1 bit trong G
      và tương tự cho H và h.
    • Nếu G == H và g == h, bạn có thể bỏ qua một trong số 0 và 1 và 1 và 0, vì chúng sẽ dẫn đến cùng một cây con.
  5. Tiếp tục đến nút tiếp theo và lặp lại cho đến khi bạn tìm thấy giải pháp hoặc bạn không còn nút nào để truy cập nữa.

Thứ tự mà bạn truy cập vào nút là quan trọng. Bạn nên lưu trữ các nút trong hàng đợi ưu tiên/heap sao cho nút tiếp theo luôn là nút đầu tiên có khả năng dẫn đến giải pháp tốt nhất. Điều này thực sự dễ dàng, bạn chỉ cần thực hiện cho mỗi nút G + H và tiền tố nó với số lượng cần thiết của 1 bit để đạt tới k; đó là giải pháp tốt nhất có thể từ đó.

Có thể có các quy tắc tốt hơn để loại bỏ các nút không hợp lệ (bước 2 và 3), nhưng ý tưởng của thuật toán là như nhau.

+0

Xin cảm ơn, ý tưởng thật tuyệt vời. Tuy nhiên, tôi không thực sự hiểu cách thực hiện 'A * -ness' ở đây. Điều gì sẽ là chìa khóa cho hàng đợi ưu tiên, cụ thể là trọng lượng cạnh (có lẽ là = 1 cho tất cả các cạnh) và trọng lượng heuristic. Tiền tố với số lượng cần thiết của 1-bit sẽ cung cấp cho số nhưng không có đảm bảo rằng đây là tổng của hai số nguyên với g, h 1-bit. Hơn nữa, vai trò của tiền tố như vậy cho hàng đợi/heap ưu tiên dường như bị che khuất – Atin

+1

@Atin Trọng số của min-heap sẽ là giá trị tiền tố đó. Như bạn đã nói, không có gì đảm bảo rằng bạn sẽ có thể đạt được con số đó từ một nút nhất định, nhưng bạn có đảm bảo rằng bạn sẽ không thể đạt được một giải pháp tốt hơn (nghĩa là, đó là một heuristic lạc quan), đó là những gì A * đòi hỏi phải làm việc (rõ ràng, chính xác hơn, các heuristic lạc quan càng tốt). – jdehesa

+0

Ồ, tôi hiểu rồi. Tuy nhiên, cách tiếp cận này bỏ qua trọng lượng cạnh làm cho nó trở thành một tìm kiếm tốt nhất đầu tiên tham lam hơn là tìm kiếm A *. Là trọng lượng cạnh tiềm ẩn hoặc nó thực sự là tìm kiếm tốt nhất đầu tiên? Chỉ cần đảm bảo rằng tôi hiểu mọi thứ một cách chính xác. @jdehesa – Atin

Các vấn đề liên quan