废料堆 UVa 10755题解

题意为:

有一个长方体形状的废料堆,由A*B*C个废料块组成,每一个废料块都有一个价值,可正可负。现在要在这个长方体上选择一个子长方形,使组成这个子长方形的废料块的价值之和最大。  1<= A, B, C <=20

分析:

如果采用暴力法枚举起点(x,y,z),并且枚举长宽高,则需要n^6*n^3的时间复杂度,显示会超时。因此我们记录每一个点与(1,1,1)坐标组成的长方体的值。在运算中通过枚举 x1,x2,y1,y2,x1为长方体的起始x坐标,x2为长方体的结束x坐标。y1为长方体的起始y坐标,y2为长方体的结束y坐标。并且枚举z的坐标位置,通过记录最大值和状态值进行比较。最终将时间复杂度压缩到n^5.

一般情况下可通过记录有意义的状态值来减少时间复杂度。

题解代码为:

code1:

#include<cstdio>
#include<map>
#include<string.h>
#include<iostream>
using namespace std;
const int maxn = 22;
long long cube[maxn][maxn][maxn];
void binary(int d, int& b0, int& b1, int& b2) {
    b0 = d % 2; d /= 2;
    b1 = d % 2; d /= 2;
    b2 = d % 2;
}
int sign(int d) {
    if(d%2) return 1;
    return -1;
}
long long sum(int a1, int a2, int b1, int b2, int c1, int c2) {
    long long ans = 0;
    int dx = a2 - a1 + 1,
        dy = b2 - b1 + 1,
        dz = c2 - c1 + 1;
    for(int i = 0; i < 8; i++) {
        int x0, x1, x2;
        binary(i, x0, x1, x2);
        ans -= sign(x0+x1+x2) * cube[a2-dx*x0][b2-dy*x1][c2-dz*x2];
    }
    return ans;
}
int main() {
    int A, B, C;
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d", &A, &B, &C);
        for(int a = 1; a <= A; a++) {
            for(int b = 1; b <= B; b++) {
                for(int c = 1; c <= C; c++) scanf("%lld", &cube[a][b][c]);
            }
        }
        for(int a = 1; a <= A; a++) {
            for(int b = 1; b <= B; b++) {
                for(int c = 1; c <= C; c++) {
                    for(int d = 1; d < 8; d++) {
                        int b0, b1, b2;
                        binary(d, b0, b1, b2);
                        cube[a][b][c] += sign(b0+b1+b2) * cube[a-b0][b-b1][c-b2];
                    }
                }
            }
        }
        long long ans = cube[1][1][1];
        for(int a1 = 1; a1 <= A; a1++) {
            for(int a2 = a1; a2 <= A; a2++) {
                for(int b1 = 1; b1 <= B; b1++) {
                    for(int b2 = b1; b2 <= B; b2++) {
                        long long tmin = 0;
                        for(int z = 1; z <= C; z++) {
                            long long tsum = sum(a1, a2, b1, b2, 1, z);
                            ans = max(ans, tsum - tmin);
                            tmin = min(tmin, tsum);
                        }
                    }
                }
            }
        }
        printf("%lld\n", ans);
        if(t) printf("\n");
    }
    return 0;
}

code2:

第二种方法只记录了c坐标方向的前缀和,因此最后计算和时,需要多加入一次循环累加。

#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 22;
long long cube[maxn][maxn][maxn];
int main() {
    int A, B, C;
    int t;
    scanf("%d", &t);
    memset(cube, 0, sizeof(cube));
    while(t--) {
        scanf("%d%d%d", &A, &B, &C);
        for(int a = 1; a <= A; a++) {
            for(int b = 1; b <= B; b++) {
                for(int c = 1; c <= C; c++) {
                    scanf("%lld", &cube[a][b][c]);
                    cube[a][b][c] += cube[a][b][c-1];
                }
            }
        }
        long long ans;
        ans = cube[1][1][1];
        for(int b1 = 1; b1 <= B; b1++) {
            for(int b2 = b1; b2 <= B; b2++) {
                for(int c1 = 0; c1 <= C; c1++) {
                    for(int c2 = c1 + 1; c2 <= C; c2++) {
                        long long cmax;
                        for(int a = 1; a <= A; a++) {
                            long long sum = 0;
                            for(int b = b1; b <= b2; b++) sum = sum + cube[a][b][c2] - cube[a][b][c1];
                            cmax = (a==1) ? sum : max(cmax+sum, sum);
                            ans = max(ans, cmax);
                        }
                    }
                }
            }
        }
        printf("%lld\n", ans);
        if(t) printf("\n");
    }
    return 0;
}


上述题目的一个一维形式就是求一个序列的最大连续数之和。

1. 一个整数(可正可负)序列,有A个元素。在整数序列中选择一个子序列,使得子序列的和最大。

分析:通过记录前缀和,并不断记录和更新状态。

代码为:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100 + 10;
int A[maxn], S[maxn];
int main() {
    int n;
    while(scanf("%d", &n) != EOF && n) {
        for(int a = 1; a <= n; a++) {
            scanf("%d", &A[a]);
            S[a] = S[a-1] + A[a];
        }
        int ans, cmax;
        ans = cmax = A[1];
        for(int a = 2; a <= n; a++) {
            cmax = max(cmax+A[a], A[a]);
            ans = max(ans, cmax);
        }
        printf("%d\n", ans);
    }
    return 0;
}

上述题目的二维形式如下图所示。

2. 长方形形状的物体,由A*B个块组成。每个块都有一个价值(可正可负的整数)在长方形中选择一个子长方形,使得子长方形的块的价值之和最大。

代码为:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100 + 10;
int A[maxn][maxn];
int main() {
    int m,n;
    while(scanf("%d%d", &m, &n) != EOF && m) {
        for(int a = 1; a <= m; a++) {
            for(int b = 0; b < n; b++) {
                scanf("%d", &A[a][b]);
                A[a][b] += A[a-1][b];
            }
        }
        int ans, tMax;
        ans = A[1][0];
        for(int a = 0; a <= m; a++) {
            for(int b = a + 1; b <= m; b++) {
                tMax = A[b][0] - A[a][0];
                for(int c = 1; c < n; c++) {
                    tMax = max(tMax+A[b][c]-A[a][c], A[b][c]-A[a][c]);
                    ans = max(ans, tMax);
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

此题也可以通过记录以(x,y)坐标结尾的前缀和来解决,这样具有更高的通用性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值