P2089 烤鸡

可以用暴力的十个for循环嵌套QWQ

卡在递归上很久很久很久…

题目:

题目背景

猪猪hanke得到了一只鸡

题目描述

猪猪Hanke特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke吃鸡很特别,为什么特别呢?因为他有10种配料(芥末、孜然等),每种配料可以放1—3克,任意烤鸡的美味程度为所有配料质量之和

现在,Hanke想要知道,如果给你一个美味程度,请输出这10种配料的所有搭配方案

输入输出格式

输入格式:
一行,n < = 5000

输出格式:
第一行,方案总数

第二行至结束,10个数,表示每种配料所放的质量

按字典序排列。

如果没有符合要求的方法,就只要在第一行输出一个“0”

输入输出样例

输入样例#1:
11
输出样例#1:
10
1 1 1 1 1 1 1 1 1 2 
1 1 1 1 1 1 1 1 2 1 
1 1 1 1 1 1 1 2 1 1 
1 1 1 1 1 1 2 1 1 1 
1 1 1 1 1 2 1 1 1 1 
1 1 1 1 2 1 1 1 1 1 
1 1 1 2 1 1 1 1 1 1 
1 1 2 1 1 1 1 1 1 1 
1 2 1 1 1 1 1 1 1 1 
2 1 1 1 1 1 1 1 1 1 
说明

枚举

分析:

  • 写得low一点就是十个for嵌套,定义一个数组 food[10],每个数值从1取到3 。 最后判断数值之和与题目给的美味程度是否一致。
  • 大量for循环存在时,就可以用递归表示。
  • 递归: 要考虑它一共有几层,最后结束递归的依据是什么。
    • 不可能精确到每次递归的结果都符合要求。所以要有一个判断条件
    • …只能意会不能言传!

代码:

#include<iostream>

using namespace std;

int food[10],sum=0;

int output(int *p){
    for(int i=0;i<10;++i){
        cout<<*p<<" ";
        p++;
    }
    cout<<endl;

}

int re(int total,int beg){

    if(beg != 10){
        for(int i=1;i<=3;++i){

            food[beg]=i;

            re(total-i,beg+1);  
        }

    }   

    else if(total==0){
        output(food);
        sum++;
        return 0;
    }
    else return 0;


}




int main(){
    int n;
    cin>>n; 
    re(n,0);    
    cout<<sum<<endl;    


}

代码二 :恐惧的十连for

#include <cmath>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <string>
#include <iomanip>
#include <ctime>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;//会的都写上(写了没事,没写有事)
int a,x;
int i[10];
int main()
{
    scanf("%d",&a);
    for(i[0]=1;i[0]<=3;i[0]++)
        for(i[1]=1;i[1]<=3;i[1]++)
            for(i[2]=1;i[2]<=3;i[2]++)
                for(i[3]=1;i[3]<=3;i[3]++)
                    for(i[4]=1;i[4]<=3;i[4]++)
                        for(i[5]=1;i[5]<=3;i[5]++)
                            for(i[6]=1;i[6]<=3;i[6]++)
                                for(i[7]=1;i[7]<=3;i[7]++)
                                    for(i[8]=1;i[8]<=3;i[8]++)
                                        for(i[9]=1;i[9]<=3;i[9]++)//疯狂的·壮观的十重for循环(猪猪Hanke:醉了)
                                        {
                                            if(i[0]+i[1]+i[2]+i[3]+i[4]+i[5]+i[6]+i[7]+i[8]+i[9]==a)//用x储存方案数
                                                x++;
                                        }
    printf("%d\n",x);
    for(i[0]=1;i[0]<=3;i[0]++)
        for(i[1]=1;i[1]<=3;i[1]++)
            for(i[2]=1;i[2]<=3;i[2]++)
                for(i[3]=1;i[3]<=3;i[3]++)
                    for(i[4]=1;i[4]<=3;i[4]++)
                        for(i[5]=1;i[5]<=3;i[5]++)
                            for(i[6]=1;i[6]<=3;i[6]++)
                                for(i[7]=1;i[7]<=3;i[7]++)
                                    for(i[8]=1;i[8]<=3;i[8]++)
                                        for(i[9]=1;i[9]<=3;i[9]++)//疯狂的·壮观的十重for循环x2(猪猪Hanke:醉了x2)
                                        {
                                            if(i[0]+i[1]+i[2]+i[3]+i[4]+i[5]+i[6]+i[7]+i[8]+i[9]==a)
                                            {
                                                for(int j=0;j<10;j++)
                                                    printf("%d ",i[j]);//超级循环输出方案
                                                printf("\n");//换行
                                            }
                                        }
    /*system("pause");*///暂停个程序嘞(提交时要注释掉,不然就RE了)
    return 0;//庄严地结束程序
}
<think>好的,我现在需要解决用户关于洛谷P2089烤鸡问题的Java实现请求。首先,我要明确题目要求。根据引用[2],题目描述是:Hanke有10种配料,每种可以放1到3克,烤鸡的美味程度为所有配料质量之和。输入一个n,表示总质量,输出所有可能的配料组合。如果n不在10到30之间,则输出0。否则,输出方案数,接着是所有可能的组合。 接下来,我需要考虑如何用Java实现这个问题。常见的解法是枚举所有可能的组合,因为每种配料有3种可能(1、2、3克),总共有3^10种情况。但3^10等于59049,这在计算上是可行的,所以直接暴力枚举是可行的。 首先,我需要生成所有可能的10个数的组合,每个数在1到3之间。然后计算它们的和是否等于n。如果等于,记录下来。最后输出结果。 可能的实现步骤: 1. 检查输入n是否在10到30之间,否则输出0。 2. 生成所有可能的组合,可以用递归或循环嵌套。但因为10层循环不现实,递归更合适。 3. 使用回溯法生成组合,逐个位置尝试1、2、3,并累加当前总和,当组合填满10个时检查总和是否等于n。 4. 收集所有符合条件的组合,最后输出数量和具体组合。 另外,引用[1]中提到两种解法,直接枚举和剪枝优化。这里可能需要考虑剪枝,比如在生成过程中如果当前总和已经超过了n或者剩下的位置即使全填1也无法达到n,可以提前终止,减少递归次数。 例如,在递归过程中,当前已经选择了k种配料,总和为sum,剩下的(10 - k)种至少需要(10 - k)*1克,所以如果sum + (10 - k) > n,或者sum + (10 - k)*3 < n,就可以剪枝。 这样能有效减少不必要的递归调用,提升效率。 现在考虑Java代码结构。主函数读取输入n,判断范围。然后初始化结果列表,进行回溯搜索。最后输出结果。 可能的代码结构: - 使用一个ArrayList来存储所有符合条件的组合,每个组合用int数组或字符串表示。 - 递归函数参数包括当前配料位置、当前总和、当前组合数组。 - 剪枝条件在递归前判断,提前终止无效路径。 然后测试样例,比如输入11,应该输出10种组合,每个组合有10个数,每个数1或2,其中总和为11。 需要注意输出顺序,题目中的样例输出是按字典序排列的,所以递归中要按照顺序生成1、2、3,这样自然保持字典序。 最后,处理输出格式,先输出方案数,然后每行一个组合,数字用空格分隔。 现在编写代码时要注意效率,虽然数据量不大,但剪枝能优化时间。同时,Java中字符串拼接可能影响性能,但这里数据量小,可以接受。 可能的代码实现: 使用回溯法,逐层填充每个位置的1、2、3,并维护当前总和,当填充到第10层时检查总和是否等于n。符合条件的存入列表。 剪枝条件:在每一步,如果当前总和加上剩余位置的最小可能(全1)超过n,或者当前总和加上剩余位置最大可能(全3)小于n,就停止递归。 例如,当前在第k步,已经用了k个配料,总和为sum。剩下的10 - k个配料,最小总和是(10 - k)*1,最大是(10 - k)*3。如果sum + (10 -k)*1 >n 或者 sum + (10 -k)*3 <n,则不可能,剪枝。 这样可以减少递归次数。 现在编写Java代码: 主类读取n,检查范围,初始化结果列表,调用回溯方法,输出结果。 回溯方法的参数:当前步骤step(0到9),当前总和currentSum,当前数组current。 每次递归,循环i从1到3,设置current[step] =i,计算新的总和,检查是否剪枝,否则继续递归下一层step+1。 当step ==10时,检查currentSum是否等于n,是的话添加到结果列表。 这样实现即可。 然后处理输出,先输出结果数量,然后遍历列表输出每个组合。 测试案例: 输入11,输出应该是10种组合,每种组合总和为11,每个位置是1或2,且恰好有一个位置是2,其余是1。 例如,第一个组合是1 1 1 1 1 1 1 1 1 2,第二个是1 1 1 1 1 1 1 1 2 1,依此类推,共10种。 这样代码应该能正确生成这些组合。 最后,注意Java中数组的操作,使用Arrays.toString或者手动拼接字符串,但需要替换掉逗号为空格。 例如,将数组转为字符串,去掉逗号和括号,用空格分隔。 或者,在生成时用StringBuilder拼接每个数字加空格,最后trim掉末尾空格。 这样处理输出格式。</think>### P2089 烤鸡问题 Java 实现 #### 解法思路 本题要求枚举10种配料(每种1-3克)的所有可能组合,使得总质量等于输入值$n$。采用**回溯剪枝法**实现,在递归过程中通过以下优化提升效率: 1. **剪枝条件**:当当前总和超过$n$,或剩余配料无法达到$n$时提前终止递归 2. **字典序输出**:按自然顺序生成组合(1→2→3),保证结果直接有序 #### 完整代码 ```java import java.util.ArrayList; import java.util.Scanner; public class P2089 { static ArrayList<int[]> result = new ArrayList<>(); public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); if (n < 10 || n > 30) { System.out.println(0); return; } backtrack(new int[10], 0, 0, n); System.out.println(result.size()); for (int[] arr : result) { StringBuilder sb = new StringBuilder(); for (int num : arr) { sb.append(num).append(" "); } System.out.println(sb.toString().trim()); } } static void backtrack(int[] current, int step, int sum, int target) { if (step == 10) { if (sum == target) { result.add(current.clone()); } return; } for (int i = 1; i <= 3; i++) { int newSum = sum + i; int remainingSteps = 10 - step - 1; // 剪枝:后续步骤最小值/最大值是否可能满足条件 if (newSum + remainingSteps * 1 > target) break; if (newSum + remainingSteps * 3 < target) continue; current[step] = i; backtrack(current, step + 1, newSum, target); } } } ``` #### 代码解析 1. **输入校验**:直接排除$n<10$或$n>30$的无效情况 2. **回溯函数**: - `current`记录当前配料组合 - `step`表示当前处理到第几种配料 - `sum`为当前总质量 - 循环尝试1-3克,通过剪枝条件跳过无效分支[^1] 3. **结果输出**:使用`StringBuilder`优化字符串拼接效率 #### 测试样例 输入: ``` 11 ``` 输出: ``` 10 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 ... 2 1 1 1 1 1 1 1 1 1 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值