洛谷2530(codevs2098)化工厂装箱员

本文针对洛谷P2530题目提供了解题思路,采用搜索方法实现,详细介绍了记忆化搜索的过程及注意事项,同时对比了几种不同代码实现方式及其问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:https://www.luogu.org/problemnew/show/P2530

dp或搜索。

dp做法就是 当前值+1 转移到 当前某一维为0、位置前进了c位 的地方。但没写。

写了搜索的方法。细节众多,而且RE地莫名其妙!

搜索要注意记忆化。

特别奇怪的细节:代码中用注释(d数组)代替t1 t2 t3的话就会WA。

子函数中传参如果写成c[ ],就是不确定大小,于是不能用memcpy了。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=1005;
int n,col[105],tmp[5],f[105][15][15][15];
char ch;
int num(char ch)
{
    if(ch=='A')return 1;
    if(ch=='B')return 2;
    if(ch=='C')return 3;
}
int dfs(int c[5],int now)
{
    if(f[now][c[1]][c[2]][c[3]])return f[now][c[1]][c[2]][c[3]];
    if(!c[1]&&!c[2]&&!c[3])return 0;
    int num=INF,j=0;
    int t1=c[1],t2=c[2],t3=c[3];
//    int d[5]={0};
//    memcpy(d,c,sizeof c);
//    d[1]=c[1];d[2]=c[2];d[3]=c[3];
    for(int i=1;i<=3;i++)
    {
        if(!c[i])continue;
        int t=c[i];
        c[i]=0;
        for(j=now;j<now+t&&j<=n;j++)
            c[col[j]]++;
        num=min(num,dfs(c,j));
        c[1]=t1;c[2]=t2;c[3]=t3;
//        memcpy(c,d,sizeof d);
//        c[1]=d[1];c[2]=d[2];c[3]=d[3];
    }
    return f[now][c[1]][c[2]][c[3]]=num+1;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf(" %c",&ch);
        col[i]=num(ch);
        if(i<=10)tmp[col[i]]++;
    }
    printf("%d",dfs(tmp,min(11,n+1)));
    return 0;
}

 RE+WA+MLE代码(注意判断return 0和记忆化的那两句的位置是在补满10个以后):

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=105;
int n,col[105],tmp[5],f[105][15][15][15];
char ch;
int num(char ch)
{
    if(ch=='A')return 1;
    if(ch=='B')return 2;
    if(ch=='C')return 3;
}
int dfs(int c[5],int now)
{
    int k=10-c[1]-c[2]-c[3];
    for(int i=now;i<now+k&&i<=n;i++)
        c[col[i]]++;
    if(!c[1]&&!c[2]&&!c[3]&&now>1)return 0;
    if(f[now][c[1]][c[2]][c[3]])return f[now][c[1]][c[2]][c[3]];
    int d[5]={0},num=INF;
//    d[1]=c[1];d[2]=c[2];d[3]=c[3];
    memcpy(d,c,sizeof c);
    for(int i=1;i<=3;i++)
    {
        if(!c[i])continue;
        c[i]=0;
        num=min(num,dfs(c,now+k));
        c[i]=d[i];
    }
    return f[now][c[1]][c[2]][c[3]]=num+1;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf(" %c",&ch);
        col[i]=num(ch);
    }
    printf("%d",dfs(tmp,1));
    return 0;
}
代码2

改了改上边之后的WA代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=105;
int n,col[105],tmp[5],f[105][15][15][15];
char ch;
int num(char ch)
{
    if(ch=='A')return 1;
    if(ch=='B')return 2;
    if(ch=='C')return 3;
}
int dfs(int c[],int now,int k)
{
//    int k=10-c[1]-c[2]-c[3],l=0;
    int l=0;
    for(l=now;l<now+k&&l<=n;l++)
        c[col[l]]++;
    if(f[now][c[1]][c[2]][c[3]])return f[now][c[1]][c[2]][c[3]];
    if(!c[1]&&!c[2]&&!c[3])return 0;
    int num=INF,t1=c[1],t2=c[2],t3=c[3];
//    d[1]=c[1];d[2]=c[2];d[3]=c[3];
//    memcpy(d,c,sizeof c);
    for(int i=1;i<=3;i++)
    {
        if(!c[i])continue;
        int t=c[i];
        c[i]=0;
        num=min(num,dfs(c,l,t));
//        c[i]=d[i];
        c[1]=t1;c[2]=t2;c[3]=t3;
    }
    return f[now][c[1]][c[2]][c[3]]=num+1;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf(" %c",&ch);
        col[i]=num(ch);
    }
    printf("%d",dfs(tmp,1,10));
    return 0;
}
代码3

 

转载于:https://www.cnblogs.com/Narh/p/8671785.html

### 动态规划在装箱问题中的应用 装箱问题是典型的 **0/1 背包问题** 的变种之一。其核心在于如何利用有限的空间装载尽可能多的物品,或者使剩余空间最小化。此问题可以采用动态规划的思想来解决。 #### 问题描述 假设有一个箱子容量为 \( V \),以及若干个不同大小的物品,每个物品只能放入一次(即 0/1 背包)。目标是找到一种方案使得所选物品的总体积最接近于 \( V \) 并不超过它。 --- #### 解题思路 动态规划的核心思想是将大问题分解为多个子问题并存储中间结果以避免重复计算[^1]。对于装箱问题: - 定义状态数组 `dp[j]` 表示当背包容量为 \( j \) 时能够容纳的最大体积。 - 初始化:设初始状态下背包为空,则 `dp[0] = 0`;其余位置初始化为负无穷或零视具体需求而定。 - 状态转移方程: \[ dp[j] = \max(dp[j], dp[j - w_i] + w_i),\quad (w_i \leq j) \] 这里的 \( w_i \) 是第 \( i \) 件物品的重量(或体积)。 上述公式的含义是在当前考虑的物品下,判断是否将其加入到已有的最佳组合中去形成新的更优解。 由于题目要求的是最终未被填满的部分最少,因此最后一步还需要计算差值作为答案输出。 以下是基于以上逻辑编写的 Java 版本程序实现: ```java import java.util.Scanner; public class PackingProblem { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 输入数据读取 System.out.println("请输入容器最大容量V 和 物品数量N:"); int V = scanner.nextInt(); // 总体积极限 int N = scanner.nextInt(); // 物品种类数目 int[] weights = new int[N + 1]; for (int i = 1; i <= N; ++i){ weights[i] = scanner.nextInt(); } // 创建DP表,默认填充为0 int[] dp = new int[V + 1]; // 开始迭代更新DP表格 for (int i = 1; i <= N; ++i){ for (int j = V; j >= weights[i]; --j){ dp[j] = Math.max(dp[j], dp[j - weights[i]] + weights[i]); } } // 输出结果 System.out.println(V - dp[V]); // 剩余空间量 } } ``` 该代码实现了基本的功能框架,并遵循了标准输入输出模式以便测试不同的案例集。 --- #### 关键点解析 1. **逆序遍历的原因** 对于每一件新增加的商品,在更新整个dp数组的时候采取倒叙方式是为了防止前面已经处理过的商品再次影响本次循环的结果造成错误覆盖情况发生[^2]。 2. **时间复杂度分析** 外层循环执行次数取决于物品总数\( n \),内层则依赖于背包容量\( m \),所以整体的时间复杂度大约为O(nm)[^1]. 3. **边界条件处理** 需要注意一些特殊情况下的行为定义,例如如果没有任何东西能放进背包里怎么办?此时应该返回原始容量本身表示完全没用上任何资源。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值