Tổng lớn nhất chia hết cho n


16

Tôi đã hỏi câu hỏi này trên StackOverflow , nhưng tôi nghĩ đây là một nơi thích hợp hơn.

Đây là một vấn đề từ Giới thiệu về khóa học thuật toán :

Bạn có một mảng với số nguyên dương (mảng không cần phải được sắp xếp hoặc các phần tử duy nhất). Đề xuất thuật toán để tìm tổng các phần tử lớn nhất chia hết cho .anO(n)n

Ví dụ: . Câu trả lời là (với các yếu tố )a=[6,1,13,4,9,8,25],n=7566,13,4,8,25

Việc tìm kiếm nó trong tương đối dễ dàng bằng cách sử dụng lập trình động và lưu trữ số tiền lớn nhất với phần còn lại .O(n2)0,1,2,...,n1

Ngoài ra, nếu chúng ta hạn chế sự chú ý vào một chuỗi các yếu tố liền kề, bạn có thể dễ dàng tìm thấy chuỗi tối ưu như vậy trong thời gian , bằng cách lưu trữ một phần tổng modulo : let , với mỗi phần còn lại nhớ chỉ số lớn nhất mà S [j] \ equiv r \ pmod {n} , và sau đó cho mỗi i bạn xem xét S [j] -S [i] nơi j là chỉ số tương ứng với r = S [i] \ bmod n .n S [ i ] = a [ 0 ] + a [ 1 ] + + a [ i ] r j S [ j ] rO(n)nS[i]=a[0]+a[1]++a[i]rjS[j]r(modn)iS[j]S[i]jr=S[i]modn

Nhưng có một giải pháp thời gian O(n) cho trường hợp chung không? Bất kỳ đề xuất sẽ được đánh giá cao! Tôi xem xét điều này có một cái gì đó để đối phó với đại số tuyến tính nhưng tôi không chắc chính xác những gì.

Ngoài ra, điều này có thể được thực hiện trong thời gian O(nlogn) không?


2
1. Bạn đã đăng chính xác câu hỏi tương tự trên Stack Overflow. Xin vui lòng không đăng cùng một câu hỏi trên nhiều trang web . Chúng tôi không muốn nhiều bản sao trôi nổi trên nhiều trang SE. Nếu bạn không nhận được câu trả lời chấp nhận được, bạn có thể gắn cờ câu hỏi của mình để di chuyển sang một trang web khác, nhưng vui lòng không đăng lại điều tương tự ở nơi khác. 2. Bạn có thể đưa ra một tài liệu tham khảo / trích dẫn / liên kết đến sách giáo khoa hoặc khóa học xuất hiện ở đây không? Làm thế nào chắc chắn rằng bạn có tồn tại một giải pháp thời gian O(n) ?
DW

5
Là thách thức trên trường đại học của bạn vẫn còn mở? Sẽ rất hữu ích khi thấy liên kết đến khóa học, câu hỏi chính xác và nếu nó thực sự là và những người đã chuẩn bị nó sẽ giải thích / công bố câu trả lời của họ thì thật tuyệt vời. O(n)
Ác

Việc tìm thấy nó trong O (n2) O (n2) tương đối dễ dàng bằng cách sử dụng lập trình động và lưu trữ số tiền lớn nhất với phần còn lại 0,1,2, ..., n 10,1,2, ..., n 1. Bạn có thể vui lòng giải thích điều này một chút? Tôi có thể hiểu làm thế nào điều này sẽ được bình phương nếu chúng ta chỉ xem xét các yếu tố tiếp giáp, nhưng với các yếu tố không tiếp giáp là tốt, nó sẽ không theo cấp số nhân?
Nithish Inpursuit Ofhappiness 13/07/17

Câu trả lời:


4

Dưới đây là một vài ý tưởng ngẫu nhiên:

  • Thuật toán lập trình động có thể được lật để tìm kiếm một khoản tiền nhỏ nhất thay vì một khoản tiền lớn nhất. Cuối cùng, bạn chỉ cần tìm một tổng số tương ứng với phần còn lại của tổng của toàn bộ mảng, thay vì một đồng dư bằng không. Nếu chúng ta xử lý các phần tử theo thứ tự tăng dần, điều này đôi khi cho phép thuật toán động kết thúc trước khi xử lý toàn bộ mảng.

    Chi phí sẽ là nếu chúng tôi xử lý các phần tử . Có không một giới hạn thấp hơn của trên thuật toán này bởi vì chúng tôi không cần phải sắp xếp tất cả các yếu tố. Chỉ mất thời gian để có được phần tử nhỏ nhất.k Ω ( n log n ) O ( n log k ) kO(nk)kΩ(nlogn)O(nlogk)k

  • Nếu chúng ta quan tâm đến tập hợp với kích thước lớn hơn, thay vì tập hợp có tổng tiền lớn nhất, chúng ta có thể sử dụng phép nhân đa thức dựa trên biến đổi nhanh để giải quyết vấn đề trong thời gian. Tương tự như những gì được thực hiện trong 3SUM khi phạm vi miền bị giới hạn. (Lưu ý: sử dụng bình phương lặp lại để thực hiện tìm kiếm nhị phân, nếu không bạn sẽ nhận được trong đó là số phần tử bị bỏ qua.)O ( n k ( log n ) ( log log n ) ) kO(n(logn)2(loglogn))O(nk(logn)(loglogn))k

  • Khi là hợp số và hầu hết tất cả các phần dư là một trong nhiều yếu tố của , thời gian đáng kể có thể được lưu lại bằng cách tập trung vào phần còn lại không phải là bội số của yếu tố đó.nnn

  • Khi phần còn lại rrất phổ biến hoặc chỉ có một vài phần còn lại, việc theo dõi 'khe mở tiếp theo nếu bạn bắt đầu từ đây và tiếp tục tiến lên r' thông tin có thể tiết kiệm rất nhiều điểm quét để nhảy vào các điểm mở thời gian.

  • Bạn có thể cạo một yếu tố nhật ký bằng cách chỉ theo dõi khả năng tiếp cận và sử dụng mặt nạ bit (trong thuật toán động lật), sau đó quay lại sau khi bạn đạt được mục tiêu còn lại.

  • Thuật toán lập trình động rất phù hợp để được chạy song song. Với bộ xử lý cho từng khe đệm, bạn có thể xuống . Ngoài ra, bằng cách sử dụng chiều rộng, và chia và chinh phục tập hợp thay vì tổng hợp lặp, chi phí độ sâu mạch có thể giảm xuống .O ( n 2 ) O ( log 2 n )O(n)O(n2)O(log2n)

  • (Meta) Tôi hoàn toàn nghi ngờ rằng vấn đề bạn đưa ra là về các khoản tiền tiếp giáp nhau . Nếu bạn liên kết với vấn đề thực tế, sẽ dễ dàng xác minh điều đó. Mặt khác, tôi rất ngạc nhiên bởi vấn đề này khó khăn đến mức nào, vì nó đã được chỉ định trong một khóa học gọi là "Giới thiệu về thuật toán". Nhưng có lẽ bạn đã che đậy một mánh khóe trong lớp khiến nó trở nên tầm thường.


Đối với điểm một. Nó không được viết trong thông số kỹ thuật của vấn đề, vì vậy bạn không thể cho rằng. Ngoài ra, vấn đề không phải là bạn không thể sửa đổi mảng hoặc tạo mảng mới, bạn thực sự có thể. Điều duy nhất bạn cần làm là tìm các số cộng lại với nhau cho bạn tổng lớn nhất chia hết cho trong độ phức tạp thời gian (thường là giả sử chỉ là độ phức tạp thời gian). O ( n )nO(n)
nbro

2
@EvilJS Tập hợp con có tổng lớn nhất với phần dư 0 bằng với tập hợp đầy đủ sau khi xóa tập hợp con có tổng nhỏ nhất với phần còn lại tương ứng với tổng của tập hợp đầy đủ. Tìm kiếm một tổng nhỏ nhất cho sẽ thuận tiện hơn so với tìm kiếm một số tiền lớn nhất cho vì nó cho phép bạn chấm dứt ngay khi bạn tìm thấy giải pháp (khi xử lý các phần tử theo thứ tự tăng dần) thay vì phải tiếp tục. r 2r1r2
Craig Gidney

-1

Thuật toán đề xuất của tôi diễn ra như sau:

Một tổng chia hết cho n nếu bạn chỉ thêm các triệu hồi là bội số của n.

Trước khi bắt đầu, bạn tạo một hashmap với int là khóa và danh sách các chỉ mục là giá trị. Bạn cũng tạo một danh sách kết quả có chứa các chỉ số.

Sau đó, bạn lặp qua mảng và thêm mọi chỉ số mà mod n bằng 0 vào danh sách kết quả của bạn. Đối với mọi chỉ số khác, bạn làm như sau:

Bạn trừ giá trị mod n của chỉ số này từ n. Kết quả này là chìa khóa cho hashmap của bạn, nơi lưu trữ các chỉ số cho các phần tử có giá trị bắt buộc. Bây giờ, bạn thêm chỉ mục này vào danh sách trong hashmap và tiếp tục.

Sau khi bạn hoàn thành việc lặp qua mảng, bạn tính toán đầu ra. Bạn làm điều này bằng cách sắp xếp từng danh sách trong hashmap theo giá trị mà chỉ mục trỏ tới. Bây giờ bạn xem xét mọi cặp trong hàm băm tổng hợp lên đến n. Vì vậy, nếu n = 7, bạn tìm kiếm hàm băm cho 3 và 4. Nếu bạn có một mục trong cả hai, bạn lấy hai giá trị lớn nhất xóa chúng khỏi danh sách của chúng và thêm chúng vào danh sách kết quả của bạn.

Đề xuất cuối cùng: vẫn chưa kiểm tra thuật toán, hãy viết một testcase chống lại nó bằng thuật toán vũ lực.


2
Tham lam, tuyến tính, không làm việc. Bạn chỉ xem xét các phần tử chia hết cho n và các cặp chia hết cho n, còn bộ ba và hơn thế nữa thì sao? Nó không đảm bảo tổng tập con tối đa trong trường hợp tầm thường. [2, 1, 8] -> tổng tối đa là 9, nhưng thuật toán của bạn trả về 3.
Evil

@EvilJS gì đã xảy ra với phụ bạn thuật toán? n2
Kẻ hủy diệt delta

Cảm ơn đã chỉ ra lỗi này cho tôi. Ý tưởng của tôi về cải tiến sẽ là tạo ra một sơ đồ gồm các ngăn xếp danh sách được sắp xếp bằng cách tăng giá trị và chỉ bắt đầu tích lũy sau khi hoàn thành việc vượt qua mảng.
Tobias Wurfl

Ý bạn là mảng mảng sẽ được sắp xếp và "hashmap" là% n? Bạn vẫn cần sắp xếp chúng, và nếu bạn đã sắp xếp chúng, lấy giá trị tối thiểu / tối đa là được, nhưng vẫn có một phần không thể tránh khỏi khi thực sự chọn tập hợp con, trong trường hợp xấu nhất không có lợi. Dù sao nếu bạn có một số cải tiến, có lẽ bạn có thể chỉnh sửa bài?
Ác

Vâng là một ý tưởng khá nhanh chóng với các ngăn xếp. Trong thực tế, bạn chỉ cần danh sách trong hashmap mà bạn sắp xếp. Tôi không chắc chắn rằng nó là lịch sự để chỉnh sửa câu trả lời đầu tiên của tôi. Sau tất cả, tôi đã phạm sai lầm trong lần thử đầu tiên.
Tobias Wurfl 10/03/2016

-2

sử dụng phương thức DP này từ ( /programming/4487438/maximum-sum-of-non-consceed-elements?rq=1 ):

Cho một mảng A [0..n], đặt M (i) là giải pháp tối ưu bằng cách sử dụng các phần tử có chỉ số 0..i. Khi đó M (-1) = 0 (được sử dụng trong lần tái phát), M (0) = A [0] và M (i) = max (M (i - 1), M (i - 2) + A [i ]) cho i = 1, ..., n. M (n) là giải pháp chúng tôi muốn. Đây là O (n) . Bạn có thể sử dụng một mảng khác để lưu trữ lựa chọn nào được thực hiện cho mỗi bài toán con và do đó phục hồi các phần tử thực tế đã chọn.

Thay đổi đệ quy thành M (i) = max (M (i - 1), M (i - 2) + A [i]) sao cho chỉ được lưu trữ nếu chia hết cho N


2
Điều này không hiệu quả - Tôi sẽ cho bạn biết lý do tại sao. (Gợi ý: Cố gắng chạy nó trên mảng 1 không đổi.) Ngoài ra, trong bài toán này, chúng tôi cho phép các phần tử liên tiếp.
Yuval Filmus

1
Đây là giải pháp rất tốt, chỉ là vấn đề hoàn toàn khác (và dễ dàng hơn nhiều).
Ác
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.