简答
2026-09-06 17:46:09
发布于:湖南
大概意思是这样的:
敲砖块是一道三维动态规划题目。我们先预处理出每一列敲下若干砖块可以获得的分数,用 dp 数组记录状态:处理到第几列、该列敲掉几块砖、累计一共敲多少块砖时,可以得到的最大分数。题目有一条重要规则:如果当前一列敲下 j 块砖,那么前一列至少要敲 j‑1 块,否则砖块没有支撑,属于非法状态。
很多人看懂思路之后,心里会想:我明白了,看我的。于是快速写完代码,逻辑看着没有问题。但是提交评测之后,就会发现部分测试点答案错误。
下面是当初出错的代码,问题出在数组初始化部分:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 55;
const int MAXM = 130;
const int INF = 0x3f3f3f3f;
int a[MAXN][MAXN];
int s[MAXN][MAXM];
int dp[MAXN][MAXN][MAXM];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n - i + 1; j++)
{
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= i; j++)
{
s[i][j] = s[i][j-1] + a[j][i - j + 1];
}
}
// 错误位置
memset(dp,-0x3f,sizeof dp);
dp[0][0][0] = 0;
int ans = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= i; j++)
{
for(int k = j; k <= m; k++)
{
int pk = k - j;
for(int t = max(0, j - 1); t < i; t++)
{
if(dp[i-1][t][pk] != -INF)
{
dp[i][j][k] = max(dp[i][j][k], dp[i-1][t][pk] + s[i][j]);
}
}
ans = max(ans, dp[i][j][k]);
}
}
}
cout << ans << endl;
return 0;
}
提交之后,小的样例可以输出正确结果,但部分测试点直接答案错误。没有编译报错,程序可以正常运行,只是计算结果不对。
错误原因
memset 函数是按照单个字节来填充内存的。
int 类型占用 4 个字节。当使用memset(dp,-0x3f,sizeof dp),它只会把数组内存里每一个字节设置为对应数值,并不会完整对 4 字节的 int 变量赋值负无穷。
对于小规模样例,很多无效状态不会参与计算,所以结果碰巧正确。遇到大规模测试数据时,被 memset 篡改后的异常数值会参与 max 比较,就会产生错误答案。
memset 适合用来操作 char 类型数组。不要直接用 memset 给 int 数组设置无穷、负无穷。稳妥的处理方式,使用循环遍历 dp 数组,手动给每一个元素赋值‑INF。。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[55][55];
int s[55][55];
int dp[55][55][2505];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n-i+1;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n-i+1;j++){
s[i][j]=s[i][j-1]+a[i][j];
}
}
memset(dp,-0x3f,sizeof(dp));
dp[n+1][0][0]=0;
for(int j=n;j>=1;j--){
for(int i=0;i<=n-j+1;i++){
for(int k=i;k<=m;k++){
for(int p=max(0,i-1);p<=n-j;p++){
dp[j][i][k]=max(dp[j][i][k],dp[j+1][p][k-i]+s[j][i]);
}
}
}
}
int ans=0;
for(int i=0;i<=n;i++){
ans=max(ans,dp[1][i][m]);
}
cout<<ans<<"\n";
return 0;
}
思路说明
状态dp[j][i][k]:处理第 j 列,本列敲掉 i 块砖,一共敲 k 块砖时的最大得分。
循环从最后一列往第一列倒推。边界条件dp[n+1][0][0]=0,代表后面已经没有砖块,敲 0 块得分为 0。
约束条件不变:当前列敲 i 块,则右侧相邻列至少敲i‑1块。
最终答案枚举第一列敲砖数量,取出总共敲 m 块时的最大分数。
总结
memset 操作内存的单位是字节,对 int 数组做无穷初始化有风险。小样例有可能掩盖 bug,只有大数据才会暴露错误,写代码的时候要避开这个坑。
总之来讲,题目并不简单。
这里空空如也








有帮助,赞一个