วิธีค้นหาชุดเหรียญทั้งหมดเมื่อได้รับค่าเงินดอลลาร์


114

ฉันพบโค้ดส่วนหนึ่งที่ฉันเขียนเพื่อเตรียมสัมภาษณ์เมื่อหลายเดือนก่อน

ตามความคิดเห็นที่ฉันมีกำลังพยายามแก้ปัญหานี้:

ระบุมูลค่าดอลลาร์เป็นเซนต์ (เช่น 200 = 2 ดอลลาร์ 1000 = 10 ดอลลาร์) ให้ค้นหาการรวมกันของเหรียญทั้งหมดที่รวมกันเป็นมูลค่าดอลลาร์ อนุญาตให้ใช้ได้เฉพาะเพนนี (1 ¢) นิกเกิล (5 ¢) สลึง (10 ¢) และควอเตอร์ (25 ¢) เท่านั้น

ตัวอย่างเช่นหากได้รับ 100 คำตอบควรเป็น:

4 quarter(s) 0 dime(s) 0 nickel(s) 0 pennies  
3 quarter(s) 1 dime(s) 0 nickel(s) 15 pennies  
etc.

ฉันเชื่อว่าสิ่งนี้สามารถแก้ไขได้ทั้งแบบวนซ้ำและแบบวนซ้ำ วิธีแก้ปัญหาแบบวนซ้ำของฉันค่อนข้างมีปัญหาและฉันก็สงสัยว่าคนอื่นจะแก้ปัญหานี้อย่างไร ส่วนที่ยากของปัญหานี้คือการทำให้มีประสิทธิภาพมากที่สุด


6
@akappa: เพนนี = 1 เซ็นต์; นิกเกิล = 5 เซนต์; ค่าเล็กน้อย = 10 เซนต์; ไตรมาส = 25 เซนต์ :)
codingbear

@ จอห์น T: โค๊ดกอล์ฟ? ฉันไม่เคยได้ยินคำนี้มาก่อน! อย่างไรก็ตามฉันหวังว่าจะได้รับคำตอบที่น่าสนใจเนื่องจากชุมชน SO สามารถแก้ปัญหาได้
codingbear

ฉันจะพยายามโพสต์คำตอบเมื่อกลับถึงบ้าน ... ยังอยู่ที่ทำงานและไม่ควรใช้เวลากับ SO มากเกินไป
codingbear

1
@blee code golf หมายถึงการแก้ปัญหาโดยใช้จำนวนอักขระน้อยที่สุดเท่าที่จะเป็นไปได้ด้วยภาษาโปรแกรมที่คุณเลือก นี่คือบางส่วนที่ได้ดำเนินการบนเว็บไซต์นี้: stackoverflow.com/search?q=code+golf
John T

คำตอบ:


54

ฉันตรวจสอบเรื่องนี้เมื่อนานมาแล้วและคุณสามารถอ่านบทความเล็ก ๆ น้อย ๆของฉันได้ นี่คือที่มา Mathematica

เมื่อใช้ฟังก์ชันการสร้างคุณจะได้รับวิธีแก้ปัญหาค่าคงที่รูปแบบปิดสำหรับปัญหา Graham, Knuth และ Patashnik's Concrete Mathematicsเป็นหนังสือเล่มนี้และมีการอภิปรายเกี่ยวกับปัญหาที่ค่อนข้างครอบคลุม โดยพื้นฐานแล้วคุณกำหนดพหุนามโดยที่ค่าสัมประสิทธิ์nคือจำนวนวิธีในการเปลี่ยนแปลงสำหรับnดอลลาร์

หน้า 4-5 ของการเขียนแสดงวิธีที่คุณสามารถใช้ Mathematica (หรือระบบพีชคณิตของคอมพิวเตอร์ที่สะดวกอื่น ๆ ) เพื่อคำนวณคำตอบสำหรับ 10 ^ 10 ^ 6 ดอลลาร์ในเวลาไม่กี่วินาทีในสามบรรทัดของโค้ด

(และนี่ก็นานพอที่จะเป็นสองสามวินาทีบน Pentium 75Mhz ... )


16
คำตอบที่ดี แต่มีการเล่นซ้ำเล็กน้อยโปรดทราบว่า (1) สิ่งนี้ให้จำนวนวิธีในขณะที่ด้วยเหตุผลบางประการคำถามจะถามถึงชุดที่แท้จริงของทุกวิธี แน่นอนว่าไม่มีวิธีใดที่จะหาเซตในเวลาพหุนามได้เนื่องจากผลลัพธ์นั้นมีหลายรายการมาก (2) เป็นที่ถกเถียงกันว่าฟังก์ชันการสร้างเป็น "รูปแบบปิด" หรือไม่ (ดูหนังสือGeneratingfunctionology ที่ยอดเยี่ยมของเฮอร์เบิร์ตวิลฟ์: คณิตศาสตร์ upenn.edu/~wilf/DownldGF.html ) และถ้าคุณหมายถึงนิพจน์เช่น (1 + √5) ^ n ต้องใช้เวลาΩ (log n) ในการคำนวณไม่ใช่เวลาคงที่
ShreevatsaR

การแนะนำโปรแกรมไดนามิกอย่างนุ่มนวล นอกจากนี้ผมขอแนะนำให้ทุกคนที่มีปัญหาลำดับเพื่ออ่านgeneratingfunctionology
พันเอก Panic

ขอบคุณแอนดรูว์มาก ... คำอธิบายนี้ช่วยฉันได้มาก ... การโพสต์ฟังก์ชั่นสกาลาด้านล่าง .. บางคนควรต้องการ
jayaram S

1
ฉันเชื่อว่าคำถามในตอนเริ่มต้นต้องการการแก้ไขเล็กน้อยเพราะถามว่า "... ใช้เหรียญ 1-, 10-, 25-, 50- และ 100 เซ็นต์?" แต่แล้วเขียนขึ้นมากำหนดที่ตั้งไว้aเป็นโดเมนของแต่f a = {1,5,10,25,50,100}ควรมี 5- ในรายการเหรียญสตางค์ มิฉะนั้นการเขียนจะยอดเยี่ยมมากขอบคุณ!
rbrtl

@rbrtl ว้าวคุณพูดถูกขอบคุณที่สังเกตเห็น! ฉันจะอัปเดต…
andrewdotn

42

หมายเหตุ : แสดงจำนวนวิธีเท่านั้น

ฟังก์ชัน Scala:

def countChange(money: Int, coins: List[Int]): Int =
  if (money == 0) 1
  else if (coins.isEmpty || money < 0) 0
  else countChange(money - coins.head, coins) + countChange(money, coins.tail)

1
มีวิธีเดียวในการเปลี่ยน 0 หรือไม่? ฉันเดาว่าไม่มีทางทำอย่างนั้น
ลุ

2
n1 * coins(0) + n2 * coins(1) + ... + nN * coins(N-1) = moneyมันเกิดจากจำนวนของโซลูชั่นพหุนาม ดังนั้นสำหรับmoney=0และcoins=List(1,2,5,10)นับสำหรับการรวม(n1, n2, n3, n4)คือ 1 (0, 0, 0, 0)และการแก้ปัญหาคือ
คีร์

3
ฉันไม่สามารถคาดเดาได้ว่าทำไมการใช้งานนี้จึงได้ผล ใครช่วยอธิบายขั้นตอนวิธีเบื้องหลังให้ฉันได้ไหม
Adrien Lemaire

3
นี่เป็นคำตอบที่แน่นอนสำหรับปัญหาที่ 3 ของการออกกำลังกาย 1 ของหลักสูตร scala
Justin Standard

ฉันเชื่อว่าถ้าmoney == 0แต่coins.isEmptyก็ไม่ควรนับเป็นโซลน ดังนั้นอัลโกอาจได้รับการบริการที่ดีกว่าหากcoins.isEmpty || money < 0เงื่อนไขเป็นอย่างแรก
juanchito

26

ฉันชอบวิธีแก้ปัญหาแบบวนซ้ำ คุณมีรายชื่อบางนิกายถ้านิกายที่เล็กที่สุดสามารถหารจำนวนสกุลเงินที่เหลือเท่า ๆ กันได้สิ่งนี้จะใช้ได้

โดยพื้นฐานแล้วคุณจะย้ายจากนิกายที่ใหญ่ที่สุดไปยังนิกายที่เล็กที่สุด
ซ้ำ

  1. คุณมียอดรวมปัจจุบันที่ต้องเติมและนิกายที่ใหญ่ที่สุด (มีมากกว่า 1 ด้านซ้าย) หากเหลือเพียง 1 นิกายจะมีทางเดียวเท่านั้นที่จะเติมเต็มทั้งหมด คุณสามารถใช้ 0 ถึง k สำเนาของนิกายปัจจุบันของคุณเช่น k * cur นิกาย <= total
  2. สำหรับ 0 ถึง k ให้เรียกใช้ฟังก์ชันด้วยจำนวนรวมที่แก้ไขและค่าใหม่ที่ใหญ่ที่สุด
  3. เพิ่มผลลัพธ์จาก 0 ถึง k นั่นคือจำนวนวิธีที่คุณสามารถเติมยอดรวมของคุณจากนิกายปัจจุบันลงไป ส่งคืนหมายเลขนี้

นี่คือเวอร์ชัน python ของปัญหาที่คุณระบุไว้ 200 เซ็นต์ ฉันได้รับ 1463 วิธี เวอร์ชันนี้จะพิมพ์ชุดค่าผสมทั้งหมดและจำนวนรวมสุดท้าย

#!/usr/bin/python

# find the number of ways to reach a total with the given number of combinations

cents = 200
denominations = [25, 10, 5, 1]
names = {25: "quarter(s)", 10: "dime(s)", 5 : "nickel(s)", 1 : "pennies"}

def count_combs(left, i, comb, add):
    if add: comb.append(add)
    if left == 0 or (i+1) == len(denominations):
        if (i+1) == len(denominations) and left > 0:
           if left % denominations[i]:
               return 0
           comb.append( (left/denominations[i], demoninations[i]) )
           i += 1
        while i < len(denominations):
            comb.append( (0, denominations[i]) )
            i += 1
        print(" ".join("%d %s" % (n,names[c]) for (n,c) in comb))
        return 1
    cur = denominations[i]
    return sum(count_combs(left-x*cur, i+1, comb[:], (x,cur)) for x in range(0, int(left/cur)+1))

count_combs(cents, 0, [], None)

ยังไม่ได้วิ่ง แต่ใช้ตรรกะของคุณมันก็สมเหตุสมผล :)
codingbear

คุณสามารถแทนที่สองบรรทัดสุดท้ายของฟังก์ชันด้วย "return sum (count_combs (... ) for ... )" ซึ่งวิธีนี้จะทำให้รายการไม่ปรากฏขึ้นเลย :)
Nick Johnson

ขอบคุณสำหรับทิป. ฉันสนใจวิธีการกระชับโค้ดเสมอ
leif

2
ตามที่กล่าวไว้ในคำถามอื่นรหัสนี้จะให้ผลลัพธ์ที่ไม่ถูกต้องหากรายการdenominationsไม่มี1เป็นค่าสุดท้าย คุณสามารถเพิ่มโค้ดจำนวนเล็กน้อยในifบล็อกด้านในสุดเพื่อแก้ไขได้ (ตามที่ฉันอธิบายไว้ในคำตอบของคำถามอื่น)
Blckknght

12

ฟังก์ชัน Scala:

def countChange(money: Int, coins: List[Int]): Int = {

def loop(money: Int, lcoins: List[Int], count: Int): Int = {
  // if there are no more coins or if we run out of money ... return 0 
  if ( lcoins.isEmpty || money < 0) 0
  else{
    if (money == 0 ) count + 1   
/* if the recursive subtraction leads to 0 money left - a prefect division hence return count +1 */
    else
/* keep iterating ... sum over money and the rest of the coins and money - the first item and the full set of coins left*/
      loop(money, lcoins.tail,count) + loop(money - lcoins.head,lcoins, count)
  }
}

val x = loop(money, coins, 0)
Console println x
x
}

ขอบคุณ! นี่เป็นการเริ่มต้นที่ยอดเยี่ยม แต่ฉันคิดว่าสิ่งนี้ล้มเหลวเมื่อ "เงิน" เริ่มเป็น 0 :)
aqn

10

นี่คือรหัส C ++ ที่ตรงไปตรงมาอย่างแน่นอนเพื่อแก้ปัญหาซึ่งขอให้แสดงชุดค่าผสมทั้งหมด

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("usage: change amount-in-cents\n");
        return 1;
    }

    int total = atoi(argv[1]);

    printf("quarter\tdime\tnickle\tpenny\tto make %d\n", total);

    int combos = 0;

    for (int q = 0; q <= total / 25; q++)
    {
        int total_less_q = total - q * 25;
        for (int d = 0; d <= total_less_q / 10; d++)
        {
            int total_less_q_d = total_less_q - d * 10;
            for (int n = 0; n <= total_less_q_d / 5; n++)
            {
                int p = total_less_q_d - n * 5;
                printf("%d\t%d\t%d\t%d\n", q, d, n, p);
                combos++;
            }
        }
    }

    printf("%d combinations\n", combos);

    return 0;
}

แต่ฉันค่อนข้างทึ่งเกี่ยวกับปัญหาย่อยของการคำนวณจำนวนชุดค่าผสม ฉันสงสัยว่ามีสมการรูปแบบปิดสำหรับมัน


9
แน่นอนว่านี่คือ C ไม่ใช่ C ++
nikhil

1
@ George Phillips คุณอธิบายได้ไหม?
ทดลองใช้

ฉันคิดว่ามันค่อนข้างตรงไปตรงมา โดยพื้นฐานแล้วแนวคิดคือการวนซ้ำทุกไตรมาส (โดยใช้ 0,1,2 .. สูงสุด) แล้ววนซ้ำทุกสลึงตามไตรมาสที่ใช้เป็นต้น
Peter Lee

4
ข้อเสียของวิธีนี้คือ: ถ้ามีเหรียญ 50 เซ็นต์ 100 เซ็นต์ 500 เซ็นต์เราก็ต้องใช้ลูป 6 ระดับ ...
ปีเตอร์ลี

3
มันค่อนข้างแย่ถ้าคุณมีนิกายไดนามิกหรือคุณต้องการเพิ่มนิกายอื่นมันจะไม่ได้ผล
shinzou

7

ปัญหาย่อยเป็นปัญหาการเขียนโปรแกรมแบบไดนามิกทั่วไป

/* Q: Given some dollar value in cents (e.g. 200 = 2 dollars, 1000 = 10 dollars),
      find the number of combinations of coins that make up the dollar value.
      There are only penny, nickel, dime, and quarter.
      (quarter = 25 cents, dime = 10 cents, nickel = 5 cents, penny = 1 cent) */
/* A:
Reference: http://andrew.neitsch.ca/publications/m496pres1.nb.pdf
f(n, k): number of ways of making change for n cents, using only the first
         k+1 types of coins.

          +- 0,                        n < 0 || k < 0
f(n, k) = |- 1,                        n == 0
          +- f(n, k-1) + f(n-C[k], k), else
 */

#include <iostream>
#include <vector>
using namespace std;

int C[] = {1, 5, 10, 25};

// Recursive: very slow, O(2^n)
int f(int n, int k)
{
    if (n < 0 || k < 0)
        return 0;

    if (n == 0)
        return 1;

    return f(n, k-1) + f(n-C[k], k); 
}

// Non-recursive: fast, but still O(nk)
int f_NonRec(int n, int k)
{
    vector<vector<int> > table(n+1, vector<int>(k+1, 1));

    for (int i = 0; i <= n; ++i)
    {
        for (int j = 0; j <= k; ++j)
        {
            if (i < 0 || j < 0) // Impossible, for illustration purpose
            {
                table[i][j] = 0;
            }
            else if (i == 0 || j == 0) // Very Important
            {
                table[i][j] = 1;
            }
            else
            {
                // The recursion. Be careful with the vector boundary
                table[i][j] = table[i][j-1] + 
                    (i < C[j] ? 0 : table[i-C[j]][j]);
            }
        }
    }

    return table[n][k];
}

int main()
{
    cout << f(100, 3) << ", " << f_NonRec(100, 3) << endl;
    cout << f(200, 3) << ", " << f_NonRec(200, 3) << endl;
    cout << f(1000, 3) << ", " << f_NonRec(1000, 3) << endl;

    return 0;
}

โซลูชันแบบไดนามิกของคุณต้องการให้ k มีความยาว C ลบ 1 ค่อนข้างสับสน คุณสามารถเปลี่ยนได้อย่างง่ายดายเพื่อรองรับความยาวจริงของ C.
Idan

7

โค้ดใช้ Java เพื่อแก้ปัญหานี้และก็ใช้ได้เช่นกัน ... วิธีนี้อาจไม่ใช่ความคิดที่ดีเนื่องจากมีการวนซ้ำมากเกินไป แต่เป็นวิธีที่ตรงไปตรงมา

public class RepresentCents {

    public static int sum(int n) {

        int count = 0;
        for (int i = 0; i <= n / 25; i++) {
            for (int j = 0; j <= n / 10; j++) {
                for (int k = 0; k <= n / 5; k++) {
                    for (int l = 0; l <= n; l++) {
                        int v = i * 25 + j * 10 + k * 5 + l;
                        if (v == n) {
                            count++;
                        } else if (v > n) {
                            break;
                        }
                    }
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(sum(100));
    }
}

7

นี่เป็นคำถามเก่าจริงๆ แต่ฉันคิดวิธีแก้ปัญหาซ้ำใน java ซึ่งดูเหมือนจะเล็กกว่าคำถามอื่น ๆ ทั้งหมดดังนั้นนี่ไป -

 public static void printAll(int ind, int[] denom,int N,int[] vals){
    if(N==0){
        System.out.println(Arrays.toString(vals));
        return;
    }
    if(ind == (denom.length))return;             
    int currdenom = denom[ind];
    for(int i=0;i<=(N/currdenom);i++){
        vals[ind] = i;
        printAll(ind+1,denom,N-i*currdenom,vals);
    }
 }

การปรับปรุง:

  public static void printAllCents(int ind, int[] denom,int N,int[] vals){
        if(N==0){
            if(ind < denom.length) {
                for(int i=ind;i<denom.length;i++)
                    vals[i] = 0;
            }
            System.out.println(Arrays.toString(vals));
            return;
        }
        if(ind == (denom.length)) {
            vals[ind-1] = 0;
            return;             
        }

        int currdenom = denom[ind];
        for(int i=0;i<=(N/currdenom);i++){ 
                vals[ind] = i;
                printAllCents(ind+1,denom,N-i*currdenom,vals);
        }
     }

6

ให้ C (i, J) เป็นเซตของการรวมค่า i เซ็นต์โดยใช้ค่าในเซต J

คุณสามารถกำหนด C ได้ว่า:

ใส่คำอธิบายภาพที่นี่

(ครั้งแรก (J) ใช้วิธีกำหนดองค์ประกอบของเซต)

มันกลายเป็นฟังก์ชั่นวนซ้ำที่สวยงาม ... และมีประสิทธิภาพพอสมควรหากคุณใช้การช่วยจำ;)


ใช่นี่ (ในแง่หนึ่งคือ "การเขียนโปรแกรมแบบไดนามิก") จะเป็นทางออกที่ดีที่สุด
ShreevatsaR

คุณพูดถูก: ใช้ J เป็นรายการไม่ใช่เป็นตัวตั้งอันดับแรก (J) จะนำองค์ประกอบแรกมาให้คุณและ J \ first (J) จะให้รายการที่เหลือแก่คุณ
akappa

นี่คือรูปแบบของคณิตศาสตร์อะไร?
Muhammad Umer

5

กึ่งแฮ็กเพื่อแก้ไขปัญหาการรวมกันที่ไม่ซ้ำกัน - บังคับลำดับจากมากไปหาน้อย:

สกุลเงิน $ = [1,5,10,25]
def all_combs (ผลรวมสุดท้าย) 
  ส่งคืน 1 ถ้าผลรวม == 0
  ส่งคืน $ denoms.select {| d | d & le sum && d & le last} .inject (0) {| total, denom |
           all_combs + รวม (Sum-denom, denom)}
ปลาย

สิ่งนี้จะทำงานช้าเนื่องจากจะไม่ถูกบันทึก แต่คุณจะได้รับแนวคิด


4
# short and sweet with O(n) table memory    

#include <iostream>
#include <vector>

int count( std::vector<int> s, int n )
{
  std::vector<int> table(n+1,0);

  table[0] = 1;
  for ( auto& k : s )
    for(int j=k; j<=n; ++j)
      table[j] += table[j-k];

  return table[n];
}

int main()
{
  std::cout <<  count({25, 10, 5, 1}, 100) << std::endl;
  return 0;
}

3

นี่คือคำตอบของฉันใน Python ไม่ใช้การเรียกซ้ำ:

def crossprod (list1, list2):
    output = 0
    for i in range(0,len(list1)):
        output += list1[i]*list2[i]

    return output

def breakit(target, coins):
    coinslimit = [(target / coins[i]) for i in range(0,len(coins))]
    count = 0
    temp = []
    for i in range(0,len(coins)):
        temp.append([j for j in range(0,coinslimit[i]+1)])


    r=[[]]
    for x in temp:
        t = []
        for y in x:
            for i in r:
                t.append(i+[y])
        r = t

    for targets in r:
        if crossprod(targets, coins) == target:
            print targets
            count +=1
    return count




if __name__ == "__main__":
    coins = [25,10,5,1]
    target = 78
    print breakit(target, coins)

ตัวอย่างผลลัพธ์

    ...
    1 ( 10 cents)  2 ( 5 cents)  58 ( 1 cents)  
    4 ( 5 cents)  58 ( 1 cents)  
    1 ( 10 cents)  1 ( 5 cents)  63 ( 1 cents)  
    3 ( 5 cents)  63 ( 1 cents)  
    1 ( 10 cents)  68 ( 1 cents)  
    2 ( 5 cents)  68 ( 1 cents)  
    1 ( 5 cents)  73 ( 1 cents)  
    78 ( 1 cents)  
    Number of solutions =  121

3
var countChange = function (money,coins) {
  function countChangeSub(money,coins,n) {
    if(money==0) return 1;
    if(money<0 || coins.length ==n) return 0;
    return countChangeSub(money-coins[n],coins,n) + countChangeSub(money,coins,n+1);
  }
  return countChangeSub(money,coins,0);
}

2

ทั้งสอง: วนซ้ำผ่านทุกนิกายจากสูงไปต่ำใช้หนึ่งในนิกายลบออกจากผลรวมที่ต้องการจากนั้นเรียกคืนส่วนที่เหลือ (การ จำกัด นิกายที่สามารถใช้ได้ให้เท่ากับหรือต่ำกว่าค่าการวนซ้ำในปัจจุบัน)


2

หากระบบสกุลเงินอนุญาตให้ใช้อัลกอริทึมโลภอย่างง่ายที่ใช้เหรียญแต่ละเหรียญให้ได้มากที่สุดโดยเริ่มจากสกุลเงินที่มีมูลค่าสูงสุด

มิฉะนั้นการเขียนโปรแกรมแบบไดนามิกเป็นสิ่งจำเป็นที่จะหาทางออกที่ดีที่สุดได้อย่างรวดเร็วเนื่องจากปัญหานี้เป็นหลักปัญหาเป้

ตัวอย่างเช่นหากระบบสกุลเงินมีเหรียญ: {13, 8, 1}วิธีแก้ปัญหาแบบโลภจะเปลี่ยนแปลง 24 เป็น{13, 8, 1, 1, 1}แต่ทางออกที่ดีที่สุดคือ{8, 8, 8}

แก้ไข: ฉันคิดว่าเรากำลังทำการเปลี่ยนแปลงอย่างเหมาะสมที่สุดไม่ได้ระบุวิธีการทั้งหมดในการเปลี่ยนแปลงด้วยเงินสักบาท บทสัมภาษณ์ล่าสุดของฉันถามว่าจะเปลี่ยนแปลงได้อย่างไรดังนั้นฉันจึงกระโดดไปข้างหน้าก่อนที่จะจบเพื่ออ่านคำถาม


ปัญหาไม่จำเป็นต้องใช้เงินเพียงหนึ่งดอลลาร์ - อาจเป็น 2 หรือ 23 ดังนั้นวิธีแก้ปัญหาของคุณจึงยังคงเป็นวิธีเดียวที่ถูกต้อง
Neil G

2

ฉันรู้ว่านี่เป็นคำถามเก่ามาก ฉันกำลังค้นหาคำตอบที่ถูกต้องและไม่พบสิ่งที่ง่ายและน่าพอใจ ใช้เวลาพอสมควร แต่ก็สามารถจดบางสิ่งได้

function denomination(coins, original_amount){
    var original_amount = original_amount;
    var original_best = [ ];

    for(var i=0;i<coins.length; i++){
      var amount = original_amount;
      var best = [ ];
      var tempBest = [ ]
      while(coins[i]<=amount){
        amount = amount - coins[i];
        best.push(coins[i]);
      }
      if(amount>0 && coins.length>1){
        tempBest = denomination(coins.slice(0,i).concat(coins.slice(i+1,coins.length)), amount);
        //best = best.concat(denomination(coins.splice(i,1), amount));
      }
      if(tempBest.length!=0 || (best.length!=0 && amount==0)){
        best = best.concat(tempBest);
        if(original_best.length==0 ){
          original_best = best
        }else if(original_best.length > best.length ){
          original_best = best;
        }  
      }
    }
    return original_best;  
  }
  denomination( [1,10,3,9] , 19 );

นี่คือโซลูชันจาวาสคริปต์และใช้การเรียกซ้ำ


โซลูชันนี้พบเพียงนิกายเดียว คำถามคือการหานิกาย "ทั้งหมด"
heinob

2

ในภาษาการเขียนโปรแกรม Scala ฉันจะทำเช่นนี้:

 def countChange(money: Int, coins: List[Int]): Int = {

       money match {
           case 0 => 1
           case x if x < 0 => 0
           case x if x >= 1 && coins.isEmpty => 0
           case _ => countChange(money, coins.tail) + countChange(money - coins.head, coins)

       }

  }

2

นี่เป็นอัลกอริธึมแบบเรียกซ้ำแบบง่ายที่จะเรียกเก็บเงินจากนั้นใช้ใบเรียกเก็บเงินที่น้อยลงซ้ำ ๆ จนกว่าจะถึงยอดรวมจากนั้นจะใช้เงินอีกใบในนิกายเดียวกันและเรียกซ้ำอีกครั้ง ดูตัวอย่างผลลัพธ์ด้านล่างสำหรับภาพประกอบ

var bills = new int[] { 100, 50, 20, 10, 5, 1 };

void PrintAllWaysToMakeChange(int sumSoFar, int minBill, string changeSoFar)
{
    for (int i = minBill; i < bills.Length; i++)
    {
        var change = changeSoFar;
        var sum = sumSoFar;

        while (sum > 0)
        {
            if (!string.IsNullOrEmpty(change)) change += " + ";
            change += bills[i];

            sum -= bills[i]; 
            if (sum > 0)
            {
                PrintAllWaysToMakeChange(sum, i + 1, change);
            }
        }

        if (sum == 0)
        {
            Console.WriteLine(change);
        }
    }
}

PrintAllWaysToMakeChange(15, 0, "");

พิมพ์สิ่งต่อไปนี้:

10 + 5
10 + 1 + 1 + 1 + 1 + 1
5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
5 + 5 + 1 + 1 + 1 + 1 + 1
5 + 5 + 5
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1

1

ตอนนี้ฉันรู้สึกโง่มาก ด้านล่างมีวิธีแก้ปัญหาที่ซับซ้อนเกินไปซึ่งฉันจะเก็บรักษาไว้เพราะมันเป็นวิธีแก้ปัญหา วิธีง่ายๆก็คือ:

// Generate a pretty string
val coinNames = List(("quarter", "quarters"), 
                     ("dime", "dimes"), 
                     ("nickel", "nickels"), 
                     ("penny", "pennies"))
def coinsString = 
  Function.tupled((quarters: Int, dimes: Int, nickels:Int, pennies: Int) => (
    List(quarters, dimes, nickels, pennies) 
    zip coinNames // join with names
    map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number
    map (t => t._1 + " " + t._2) // qty name
    mkString " "
  ))

def allCombinations(amount: Int) = 
 (for{quarters <- 0 to (amount / 25)
      dimes <- 0 to ((amount - 25*quarters) / 10)
      nickels <- 0 to ((amount - 25*quarters - 10*dimes) / 5)
  } yield (quarters, dimes, nickels, amount - 25*quarters - 10*dimes - 5*nickels)
 ) map coinsString mkString "\n"

นี่คือทางออกอื่น ๆ วิธีแก้ปัญหานี้ขึ้นอยู่กับการสังเกตว่าแต่ละเหรียญเป็นหลาย ๆ เหรียญดังนั้นจึงสามารถแสดงในรูปของเหรียญเหล่านั้นได้

// Just to make things a bit more readable, as these routines will access
// arrays a lot
val coinValues = List(25, 10, 5, 1)
val coinNames = List(("quarter", "quarters"), 
                     ("dime", "dimes"), 
                     ("nickel", "nickels"), 
                     ("penny", "pennies"))
val List(quarter, dime, nickel, penny) = coinValues.indices.toList


// Find the combination that uses the least amount of coins
def leastCoins(amount: Int): Array[Int] =
  ((List(amount) /: coinValues) {(list, coinValue) =>
    val currentAmount = list.head
    val numberOfCoins = currentAmount / coinValue
    val remainingAmount = currentAmount % coinValue
    remainingAmount :: numberOfCoins :: list.tail
  }).tail.reverse.toArray

// Helper function. Adjust a certain amount of coins by
// adding or subtracting coins of each type; this could
// be made to receive a list of adjustments, but for so
// few types of coins, it's not worth it.
def adjust(base: Array[Int], 
           quarters: Int, 
           dimes: Int, 
           nickels: Int, 
           pennies: Int): Array[Int] =
  Array(base(quarter) + quarters, 
        base(dime) + dimes, 
        base(nickel) + nickels, 
        base(penny) + pennies)

// We decrease the amount of quarters by one this way
def decreaseQuarter(base: Array[Int]): Array[Int] =
  adjust(base, -1, +2, +1, 0)

// Dimes are decreased this way
def decreaseDime(base: Array[Int]): Array[Int] =
  adjust(base, 0, -1, +2, 0)

// And here is how we decrease Nickels
def decreaseNickel(base: Array[Int]): Array[Int] =
  adjust(base, 0, 0, -1, +5)

// This will help us find the proper decrease function
val decrease = Map(quarter -> decreaseQuarter _,
                   dime -> decreaseDime _,
                   nickel -> decreaseNickel _)

// Given a base amount of coins of each type, and the type of coin,
// we'll produce a list of coin amounts for each quantity of that particular
// coin type, up to the "base" amount
def coinSpan(base: Array[Int], whichCoin: Int) = 
  (List(base) /: (0 until base(whichCoin)).toList) { (list, _) =>
    decrease(whichCoin)(list.head) :: list
  }

// Generate a pretty string
def coinsString(base: Array[Int]) = (
  base 
  zip coinNames // join with names
  map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number
  map (t => t._1 + " " + t._2)
  mkString " "
)

// So, get a base amount, compute a list for all quarters variations of that base,
// then, for each combination, compute all variations of dimes, and then repeat
// for all variations of nickels.
def allCombinations(amount: Int) = {
  val base = leastCoins(amount)
  val allQuarters = coinSpan(base, quarter)
  val allDimes = allQuarters flatMap (base => coinSpan(base, dime))
  val allNickels = allDimes flatMap (base => coinSpan(base, nickel))
  allNickels map coinsString mkString "\n"
}

ดังนั้นสำหรับ 37 เหรียญตัวอย่างเช่น:

scala> println(allCombinations(37))
0 quarter 0 dimes 0 nickels 37 pennies
0 quarter 0 dimes 1 nickel 32 pennies
0 quarter 0 dimes 2 nickels 27 pennies
0 quarter 0 dimes 3 nickels 22 pennies
0 quarter 0 dimes 4 nickels 17 pennies
0 quarter 0 dimes 5 nickels 12 pennies
0 quarter 0 dimes 6 nickels 7 pennies
0 quarter 0 dimes 7 nickels 2 pennies
0 quarter 1 dime 0 nickels 27 pennies
0 quarter 1 dime 1 nickel 22 pennies
0 quarter 1 dime 2 nickels 17 pennies
0 quarter 1 dime 3 nickels 12 pennies
0 quarter 1 dime 4 nickels 7 pennies
0 quarter 1 dime 5 nickels 2 pennies
0 quarter 2 dimes 0 nickels 17 pennies
0 quarter 2 dimes 1 nickel 12 pennies
0 quarter 2 dimes 2 nickels 7 pennies
0 quarter 2 dimes 3 nickels 2 pennies
0 quarter 3 dimes 0 nickels 7 pennies
0 quarter 3 dimes 1 nickel 2 pennies
1 quarter 0 dimes 0 nickels 12 pennies
1 quarter 0 dimes 1 nickel 7 pennies
1 quarter 0 dimes 2 nickels 2 pennies
1 quarter 1 dime 0 nickels 2 pennies

1

นี้รายการบล็อกของฉันแก้เป้เช่นนี้ปัญหาสำหรับตัวเลขจากการ์ตูน XKCD การเปลี่ยนคำสั่งitemsและexactcostค่าจะให้คำตอบสำหรับปัญหาของคุณด้วย

หากปัญหาคือการค้นหาการเปลี่ยนแปลงที่ใช้ต้นทุนน้อยที่สุดอัลกอริทึมโลภไร้เดียงสาที่ใช้เหรียญที่มีมูลค่าสูงสุดมากที่สุดอาจล้มเหลวในการผสมเหรียญและจำนวนเป้าหมาย ตัวอย่างเช่นหากมีเหรียญที่มีค่า 1, 3 และ 4 และจำนวนเป้าหมายคือ 6 จากนั้นอัลกอริทึมโลภอาจแนะนำสามเหรียญที่มีมูลค่า 4, 1 และ 1 เมื่อเห็นได้ง่ายว่าคุณสามารถใช้เหรียญสองเหรียญแต่ละค่าได้ 3

  • ข้าวเปลือก

1
public class Coins {

static int ac = 421;
static int bc = 311;
static int cc = 11;

static int target = 4000;

public static void main(String[] args) {


    method2();
}

  public static void method2(){
    //running time n^2

    int da = target/ac;
    int db = target/bc;     

    for(int i=0;i<=da;i++){         
        for(int j=0;j<=db;j++){             
            int rem = target-(i*ac+j*bc);               
            if(rem < 0){                    
                break;                  
            }else{                  
                if(rem%cc==0){                  
                    System.out.format("\n%d, %d, %d ---- %d + %d + %d = %d \n", i, j, rem/cc, i*ac, j*bc, (rem/cc)*cc, target);                     
                }                   
            }                   
        }           
    }       
}
 }

1

ฉันพบโค้ดที่เป็นระเบียบนี้ในหนังสือ "Python For Data Analysis" โดย O’ily มันใช้การใช้งานที่ขี้เกียจและการเปรียบเทียบ int และฉันคิดว่ามันสามารถแก้ไขได้สำหรับนิกายอื่นโดยใช้ทศนิยม แจ้งให้เราทราบวิธีการทำงานสำหรับคุณ!

def make_change(amount, coins=[1, 5, 10, 25], hand=None):
 hand = [] if hand is None else hand
 if amount == 0:
 yield hand
 for coin in coins:
 # ensures we don't give too much change, and combinations are unique
 if coin > amount or (len(hand) > 0 and hand[-1] < coin):
 continue
 for result in make_change(amount - coin, coins=coins,
 hand=hand + [coin]):
 yield result


1

นี่คือการปรับปรุงคำตอบของ Zihan การวนซ้ำที่ไม่จำเป็นจำนวนมากเกิดขึ้นเมื่อนิกายมีค่าเพียง 1 เซ็นต์

ใช้งานง่ายและไม่เรียกซ้ำ

    public static int Ways2PayNCents(int n)
    {
        int numberOfWays=0;
        int cent, nickel, dime, quarter;
        for (quarter = 0; quarter <= n/25; quarter++)
        {
            for (dime = 0; dime <= n/10; dime++)
            {
                for (nickel = 0; nickel <= n/5; nickel++)
                {
                    cent = n - (quarter * 25 + dime * 10 + nickel * 5);
                    if (cent >= 0)
                    {
                        numberOfWays += 1;
                        Console.WriteLine("{0},{1},{2},{3}", quarter, dime, nickel, cent);
                    }                   
                }
            }
        }
        return numberOfWays;            
    }

คุณไม่สามารถสรุปวิธีแก้ปัญหานี้ได้ดังนั้นตัวอย่างเช่นองค์ประกอบใหม่เกิดขึ้นในกรณีนี้คุณต้องเพิ่มอีกอันสำหรับลูป
Sumit Kumar Saha

1

โซลูชัน Java ที่ตรงไปตรงมา:

public static void main(String[] args) 
{    
    int[] denoms = {4,2,3,1};
    int[] vals = new int[denoms.length];
    int target = 6;
    printCombinations(0, denoms, target, vals);
}


public static void printCombinations(int index, int[] denom,int target, int[] vals)
{
  if(target==0)
  {
    System.out.println(Arrays.toString(vals));
    return;
  }
  if(index == denom.length) return;   
  int currDenom = denom[index];
  for(int i = 0; i*currDenom <= target;i++)
  {
    vals[index] = i;
    printCombinations(index+1, denom, target - i*currDenom, vals);
    vals[index] = 0;
  }
}

1
/*
* make a list of all distinct sets of coins of from the set of coins to
* sum up to the given target amount.
* Here the input set of coins is assumed yo be {1, 2, 4}, this set MUST
* have the coins sorted in ascending order.
* Outline of the algorithm:
* 
* Keep track of what the current coin is, say ccn; current number of coins
* in the partial solution, say k; current sum, say sum, obtained by adding
* ccn; sum sofar, say accsum:
*  1) Use ccn as long as it can be added without exceeding the target
*     a) if current sum equals target, add cc to solution coin set, increase
*     coin coin in the solution by 1, and print it and return
*     b) if current sum exceeds target, ccn can't be in the solution, so
*        return
*     c) if neither of the above, add current coin to partial solution,
*        increase k by 1 (number of coins in partial solution), and recuse
*  2) When current denomination can no longer be used, start using the
*     next higher denomination coins, just like in (1)
*  3) When all denominations have been used, we are done
*/

#include <iostream>
#include <cstdlib>

using namespace std;

// int num_calls = 0;
// int num_ways = 0;

void print(const int coins[], int n);

void combine_coins(
                   const int denoms[], // coins sorted in ascending order
                   int n,              // number of denominations
                   int target,         // target sum
                   int accsum,         // accumulated sum
                   int coins[],        // solution set, MUST equal
                                       // target / lowest denom coin
                   int k               // number of coins in coins[]
                  )
{

    int  ccn;   // current coin
    int  sum;   // current sum

    // ++num_calls;

    for (int i = 0; i < n; ++i) {
        /*
         * skip coins of lesser denomination: This is to be efficient
         * and also avoid generating duplicate sequences. What we need
         * is combinations and without this check we will generate
         * permutations.
         */
        if (k > 0 && denoms[i] < coins[k - 1])
            continue;   // skip coins of lesser denomination

        ccn = denoms[i];

        if ((sum = accsum + ccn) > target)
            return;     // no point trying higher denominations now


        if (sum == target) {
            // found yet another solution
            coins[k] = ccn;
            print(coins, k + 1);
            // ++num_ways;
            return;
        }

        coins[k] = ccn;
        combine_coins(denoms, n, target, sum, coins, k + 1);
    }
}

void print(const int coins[], int n)
{
    int s = 0;
    for (int i = 0; i < n; ++i) {
        cout << coins[i] << " ";
        s += coins[i];
    }
    cout << "\t = \t" << s << "\n";

}

int main(int argc, const char *argv[])
{

    int denoms[] = {1, 2, 4};
    int dsize = sizeof(denoms) / sizeof(denoms[0]);
    int target;

    if (argv[1])
        target = atoi(argv[1]);
    else
        target = 8;

    int *coins = new int[target];


    combine_coins(denoms, dsize, target, 0, coins, 0);

    // cout << "num calls = " << num_calls << ", num ways = " << num_ways << "\n";

    return 0;
}

1

นี่คือฟังก์ชัน C #:

    public static void change(int money, List<int> coins, List<int> combination)
    {
        if(money < 0 || coins.Count == 0) return;
        if (money == 0)
        {
            Console.WriteLine((String.Join("; ", combination)));
            return;
        }

        List<int> copy = new List<int>(coins);
        copy.RemoveAt(0);
        change(money, copy, combination);

        combination = new List<int>(combination) { coins[0] };
        change(money - coins[0], coins, new List<int>(combination));
    }

ใช้แบบนี้:

change(100, new List<int>() {5, 10, 25}, new List<int>());

มันพิมพ์:

25; 25; 25; 25
10; 10; 10; 10; 10; 25; 25
10; 10; 10; 10; 10; 10; 10; 10; 10; 10
5; 10; 10; 25; 25; 25
5; 10; 10; 10; 10; 10; 10; 10; 25
5; 5; 10; 10; 10; 10; 25; 25
5; 5; 10; 10; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 10; 25; 25; 25
5; 5; 5; 10; 10; 10; 10; 10; 10; 25
5; 5; 5; 5; 10; 10; 10; 25; 25
5; 5; 5; 5; 10; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 25; 25; 25
5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 10; 10; 25; 25
5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 10; 25; 25
5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 25; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 25
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 10
5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5; 5

ผลผลิตสวยมาก
ขอบคุณ

1

ด้านล่างนี้เป็นโปรแกรม python เพื่อค้นหาเงินทั้งหมด นี่คือโซลูชันการเขียนโปรแกรมแบบไดนามิกที่มีลำดับ (n) เวลา เงินคือ 1,5,10,25

เราข้ามจากเงินแถว 1 ไปยังแถวเงิน 25 (4 แถว) เงินแถว 1 มีการนับหากเราพิจารณาเฉพาะเงิน 1 ในการคำนวณจำนวนชุดค่าผสม เงินแถว 5 สร้างแต่ละคอลัมน์โดยการนับในแถวเงิน r สำหรับเงินสุดท้ายเดียวกันบวกกับการนับ 5 ก่อนหน้าในแถวของมันเอง (ตำแหน่งปัจจุบันลบ 5) เงินแถว 10 ใช้เงินแถว 5 ซึ่งมีการนับสำหรับทั้ง 1,5 และเพิ่มในการนับ 10 ก่อนหน้า (ตำแหน่งปัจจุบันลบ 10) เงินแถว 25 ใช้เงินแถว 10 ซึ่งมีจำนวนเงินแถว 1,5,10 บวกกับการนับ 25 ก่อนหน้า

ตัวอย่างเช่นตัวเลข [1] [12] = ตัวเลข [0] [12] + ตัวเลข [1] [7] (7 = 12-5) ซึ่งให้ผลลัพธ์เป็น 3 = 1 + 2; ตัวเลข [3] [12] = ตัวเลข [2] [12] + ตัวเลข [3] [9] (-13 = 12-25) ซึ่งให้ผลลัพธ์เป็น 4 = 0 + 4 เนื่องจาก -13 มีค่าน้อยกว่า 0

def cntMoney(num):
    mSz = len(money)
    numbers = [[0]*(1+num) for _ in range(mSz)]
    for mI in range(mSz): numbers[mI][0] = 1
    for mI,m in enumerate(money):
        for i in range(1,num+1):
            numbers[mI][i] = numbers[mI][i-m] if i >= m else 0
            if mI != 0: numbers[mI][i] += numbers[mI-1][i]
        print('m,numbers',m,numbers[mI])
    return numbers[mSz-1][num]

money = [1,5,10,25]
    num = 12
    print('money,combinations',num,cntMoney(num))

output:    
('m,numbers', 1, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
('m,numbers', 5, [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3])
('m,numbers', 10, [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4])
('m,numbers', 25, [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4])
('money,combinations', 12, 4)

0

โซลูชัน Java

import java.util.Arrays;
import java.util.Scanner;


public class nCents {



public static void main(String[] args) {

    Scanner input=new Scanner(System.in);
    int cents=input.nextInt();
    int num_ways [][] =new int [5][cents+1];

    //putting in zeroes to offset
    int getCents[]={0 , 0 , 5 , 10 , 25};
    Arrays.fill(num_ways[0], 0);
    Arrays.fill(num_ways[1], 1);

    int current_cent=0;
    for(int i=2;i<num_ways.length;i++){

        current_cent=getCents[i];

        for(int j=1;j<num_ways[0].length;j++){
            if(j-current_cent>=0){
                if(j-current_cent==0){
                    num_ways[i][j]=num_ways[i-1][j]+1;
                }else{
                    num_ways[i][j]=num_ways[i][j-current_cent]+num_ways[i-1][j];
                }
            }else{
                num_ways[i][j]=num_ways[i-1][j];
            }


        }


    }



    System.out.println(num_ways[num_ways.length-1][num_ways[0].length-1]);

}

}


0

โซลูชัน java ด้านล่างซึ่งจะพิมพ์ชุดค่าผสมต่างๆเช่นกัน เข้าใจง่าย. ไอเดียคือ

สำหรับผลรวม 5

วิธีแก้คือ

    5 - 5(i) times 1 = 0
        if(sum = 0)
           print i times 1
    5 - 4(i) times 1 = 1
    5 - 3 times 1 = 2
        2 -  1(j) times 2 = 0
           if(sum = 0)
              print i times 1 and j times 2
    and so on......

หากผลรวมที่เหลือในแต่ละวงมีค่าน้อยกว่านิกายเช่นถ้าผลรวม 1 ที่เหลือน้อยกว่า 2 ให้ทำลายลูป

รหัสที่สมบูรณ์ด้านล่าง

กรุณาแก้ไขฉันในกรณีที่ผิดพลาดใด ๆ

public class CoinCombinbationSimple {
public static void main(String[] args) {
    int sum = 100000;
    printCombination(sum);
}

static void printCombination(int sum) {
    for (int i = sum; i >= 0; i--) {
        int sumCopy1 = sum - i * 1;
        if (sumCopy1 == 0) {
            System.out.println(i + " 1 coins");
        }
        for (int j = sumCopy1 / 2; j >= 0; j--) {
            int sumCopy2 = sumCopy1;
            if (sumCopy2 < 2) {
                break;
            }
            sumCopy2 = sumCopy1 - 2 * j;
            if (sumCopy2 == 0) {
                System.out.println(i + " 1 coins " + j + " 2 coins ");
            }
            for (int k = sumCopy2 / 5; k >= 0; k--) {
                int sumCopy3 = sumCopy2;
                if (sumCopy2 < 5) {
                    break;
                }
                sumCopy3 = sumCopy2 - 5 * k;
                if (sumCopy3 == 0) {
                    System.out.println(i + " 1 coins " + j + " 2 coins "
                            + k + " 5 coins");
                }
            }
        }
    }
}

}


0

นี่คือวิธีแก้ปัญหาที่ใช้ python ที่ใช้การเรียกซ้ำและการบันทึกทำให้เกิดความซับซ้อนของ O (mxn)

    def get_combinations_dynamic(self, amount, coins, memo):
    end_index = len(coins) - 1
    memo_key = str(amount)+'->'+str(coins)
    if memo_key in memo:
        return memo[memo_key]
    remaining_amount = amount
    if amount < 0:
        return []
    if amount == 0:
        return [[]]
    combinations = []
    if len(coins) <= 1:
        if amount % coins[0] == 0:
            combination = []
            for i in range(amount // coins[0]):
                combination.append(coins[0])
            list.sort(combination)
            if combination not in combinations:
                combinations.append(combination)
    else:
        k = 0
        while remaining_amount >= 0:
            sub_combinations = self.get_combinations_dynamic(remaining_amount, coins[:end_index], memo)
            for combination in sub_combinations:
                temp = combination[:]
                for i in range(k):
                    temp.append(coins[end_index])
                list.sort(temp)
                if temp not in combinations:
                    combinations.append(temp)
            k += 1
            remaining_amount -= coins[end_index]
    memo[memo_key] = combinations
    return combinations

โอเคฉันสงสัยว่าข้างต้นมีเวลารันพหุนาม ไม่แน่ใจว่าเรามีเวลารันพหุนามได้หรือไม่ แต่สิ่งที่ฉันสังเกตเห็นคือข้างต้นทำงานได้เร็วกว่าเวอร์ชันที่ไม่ได้บันทึกในหลาย ๆ กรณี ฉันจะค้นคว้าต่อไป
lalatnayak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.