凑硬币是动态规划的一个经典例子,比如有硬币1,3,5,则组合出11有几种方案,最少硬币个数的有几种方案。
网上例子一般都是硬币组合1,3,5,如果不是1开头的就挂了,比如硬币组合是2,5,8,自己编写了支持所有硬币组合的代码。
有两个例子:
输出和等于target的最小个数硬币组合
/**
* 输出和等于target的最小个数硬币组合
* @param coins 硬币有哪几种,如{1,3,5}
* @param target 硬币和等于多少,如13
*/
private static void calcMinCoins(int [] coins, int target)
输出和等于target的所有硬币组合
/**
* 输出和等于target的所有硬币组合
* @param coins 硬币有哪几种,如{1,3,5}
* @param target 硬币和等于多少,如13
*/
private static void calcAllCoins(int [] coins, int target)
调用函数
public static void main(String[] args) {
calcMinCoins(new int[]{2,5,8}, 50);
calcAllCoins(new int[]{2,5,8}, 50);
}
具体代码如下:
/**
* 输出和等于target的最小个数硬币组合
* @param coins 硬币有哪几种,如{1,3,5}
* @param target 硬币和等于多少,如13
*/
private static void calcMinCoins(int [] coins, int target) {
Map map = new HashMap<>();
for(int i=0;i<=target;i++){
List listi = (List)map.get(i);//所有和等于i的组合
if(listi == null){//若没有则初始化
listi = new ArrayList();
map.put(i,listi);
}
if(i == 0){//所有和等于0的组合是一个空组合
listi.add(new ArrayList());
}
for(int coin : coins){
if(coin <= i){//如果和等于i,减去coin还大于等于0,则找到和等于tmp
int tmp = i - coin;//和等于tmp
List list = (List)map.get(tmp);//所有和等于tmp的组合
if(list == null){//若没有则初始化
list = new ArrayList();
map.put(tmp,list);
}
for(int j=0;j<list.size();j++){//遍历所有和等于tmp 加上coin 就等于i了
List subList = (List)list.get(j);
List tmp2 = new ArrayList(subList);
tmp2.add(coin);
//把和等于i的组合加进listi中,前提是不重复,并且个数最小,如果比已存的组合包含元素更少,则把已存组合删了
Collections.sort(tmp2);
boolean same = false;
boolean bigger = false;
boolean isMin = false;
for(int k=0;k<listi.size();k++){
List tmpk = (List)listi.get(k);
if(tmpk.size() > tmp2.size()){
isMin = true;
break;
}else{
if(JSON.toJSONString(tmpk).equals(JSON.toJSONString(tmp2))){
same = true;
break;
}
if(tmpk.size() < tmp2.size()){
bigger = true;
break;
}
}
}
if(isMin){
listi.clear();//如果比已存的组合包含元素更少,则把已存组合删了
listi.add(tmp2);//把和等于i的组合加进listi中
}else{
if(!same&&!bigger){
listi.add(tmp2);//把和等于i的组合加进listi中
}
}
}
}
}
//找到最小组合包含几个元素
int min = 0;
if(listi.size() > 0){
min = ((List)listi.get(0)).size();
}
System.out.println("所有和等于" + i + "的最小组合包含元素个数" + min+" 这些组合为" + listi);
}
}
这个代码输出
所有和等于0的最小组合包含元素个数0 这些组合为[[]]
所有和等于1的最小组合包含元素个数0 这些组合为[]
所有和等于2的最小组合包含元素个数1 这些组合为[[2]]
所有和等于3的最小组合包含元素个数0 这些组合为[]
所有和等于4的最小组合包含元素个数2 这些组合为[[2, 2]]
所有和等于5的最小组合包含元素个数1 这些组合为[[5]]
所有和等于6的最小组合包含元素个数3 这些组合为[[2, 2, 2]]
所有和等于7的最小组合包含元素个数2 这些组合为[[2, 5]]
所有和等于8的最小组合包含元素个数1 这些组合为[[8]]
所有和等于9的最小组合包含元素个数3 这些组合为[[2, 2, 5]]
所有和等于10的最小组合包含元素个数2 这些组合为[[2, 8], [5, 5]]
所有和等于11的最小组合包含元素个数4 这些组合为[[2, 2, 2, 5]]
所有和等于12的最小组合包含元素个数3 这些组合为[[2, 2, 8], [2, 5, 5]]
所有和等于13的最小组合包含元素个数2 这些组合为[[5, 8]]
所有和等于14的最小组合包含元素个数4 这些组合为[[2, 2, 2, 8], [2, 2, 5, 5]]
所有和等于15的最小组合包含元素个数3 这些组合为[[2, 5, 8], [5, 5, 5]]
所有和等于16的最小组合包含元素个数2 这些组合为[[8, 8]]
所有和等于17的最小组合包含元素个数4 这些组合为[[2, 2, 5, 8], [2, 5, 5, 5]]
所有和等于18的最小组合包含元素个数3 这些组合为[[2, 8, 8], [5, 5, 8]]
所有和等于19的最小组合包含元素个数5 这些组合为[[2, 2, 2, 5, 8], [2, 2, 5, 5, 5]]
所有和等于20的最小组合包含元素个数4 这些组合为[[2, 2, 8, 8], [2, 5, 5, 8], [5, 5, 5, 5]]。。。后略
/**
* 输出和等于target的所有硬币组合
* @param coins 硬币有哪几种,如{1,3,5}
* @param target 硬币和等于多少,如13
*/
private static void calcAllCoins(int [] coins, int target) {
Map map = new HashMap<>();
for(int i=0;i<=target;i++){
List listi = (List)map.get(i);//所有和等于i的组合
if(listi == null){//若没有则初始化
listi = new ArrayList();
map.put(i,listi);
}
if(i == 0){//所有和等于0的组合是一个空组合
listi.add(new ArrayList());
}
for(int coin : coins){
if(coin <= i){//如果和等于i,减去coin还大于等于0,则找到和等于tmp
int tmp = i - coin;//和等于tmp
List list = (List)map.get(tmp);//所有和等于tmp的组合
if(list == null){//若没有则初始化
list = new ArrayList();
map.put(tmp,list);
}
for(int j=0;j<list.size();j++){//遍历所有和等于tmp 加上coin 就等于i了
List subList = (List)list.get(j);
List tmp2 = new ArrayList(subList);
tmp2.add(coin);
//把和等于i的组合加进listi中,前提是不重复
Collections.sort(tmp2);
boolean same = false;
for(int k=0;k<listi.size();k++){
List tmpk = (List)listi.get(k);
if(JSON.toJSONString(tmpk).equals(JSON.toJSONString(tmp2))){
same = true;
break;
}
}
if(!same){
listi.add(tmp2);//把和等于i的组合加进listi中
}
}
}
}
System.out.println("所有和等于" + i + "的组合为" + listi);
}
}
这个代码输出
所有和等于0的组合为[[]]
所有和等于1的组合为[]
所有和等于2的组合为[[2]]
所有和等于3的组合为[]
所有和等于4的组合为[[2, 2]]
所有和等于5的组合为[[5]]
所有和等于6的组合为[[2, 2, 2]]
所有和等于7的组合为[[2, 5]]
所有和等于8的组合为[[2, 2, 2, 2], [8]]
所有和等于9的组合为[[2, 2, 5]]
所有和等于10的组合为[[2, 2, 2, 2, 2], [2, 8], [5, 5]]
所有和等于11的组合为[[2, 2, 2, 5]]
所有和等于12的组合为[[2, 2, 2, 2, 2, 2], [2, 2, 8], [2, 5, 5]]
所有和等于13的组合为[[2, 2, 2, 2, 5], [5, 8]]
所有和等于14的组合为[[2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 8], [2, 2, 5, 5]]
所有和等于15的组合为[[2, 2, 2, 2, 2, 5], [2, 5, 8], [5, 5, 5]]
所有和等于16的组合为[[2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 8], [2, 2, 2, 5, 5], [8, 8]]
所有和等于17的组合为[[2, 2, 2, 2, 2, 2, 5], [2, 2, 5, 8], [2, 5, 5, 5]]
所有和等于18的组合为[[2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 8], [2, 2, 2, 2, 5, 5], [2, 8, 8], [5, 5, 8]]
所有和等于19的组合为[[2, 2, 2, 2, 2, 2, 2, 5], [2, 2, 2, 5, 8], [2, 2, 5, 5, 5]]
所有和等于20的组合为[[2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 8], [2, 2, 2, 2, 2, 5, 5], [2, 2, 8, 8], [2, 5, 5, 8], [5, 5, 5, 5]]