luoguP1437 [HNOI2004]敲砖块(dp)

来源:互联网 发布:网络的负面影响规点 编辑:程序博客网 时间:2024/05/22 03:23

题目背景

题目描述

在一个凹槽中放置了 n 层砖块、最上面的一层有n 块砖,从上到下每层依次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示。

14 15 4 3 23 33 33 76 2  2 13 11   22 23    31

如果你想敲掉第 i 层的第j 块砖的话,若i=1,你可以直接敲掉它;若i>1,则你必须先敲掉第i-1 层的第j 和第j+1 块砖。

你现在可以敲掉最多 m 块砖,求得分最多能有多少。

输入输出格式

输入格式:

输入文件的第一行为两个正整数 n 和m;接下来n 行,描述这n 层砖块上的分值a[i][j],满足

0≤a[i][j]≤100。

对于 100%的数据,满足1≤n≤50,1≤m≤n*(n+1)/2;

输出格式:

输出文件仅一行为一个正整数,表示被敲掉砖块的最大价值总和。

输入输出样例

输入样例#1: 复制
4 52 2 3 48 2 72 349
输出样例#1: 复制
19

分析:
ZYXZYXZYX在我做这道题之前,告诉我这道题额转移要斜着来
一开始我还不大理解,当我看到输入数据的时候,我就尝试着把数据旋转90°
2 2 3 4
8 2 7
2 3
49
—>
4
3 7
2 2 3
2 8 2 49

这样有什么好处呢:
题目中的限制:敲掉一个就必须把上面的砖块都敲掉
就变成了每次必须选择一个前缀加入贡献,
同时要满足当前前缀的最右端点不超过上一行我们选择的前缀的最右端点+1
(请仔细品读这句不像人话的人话)
这里写图片描述

于是我就开心的设计状态:f[m][i][j]

表示当前选择了m块,最后选择的前缀是第i行的前j个

然后就A了。。。猝不及防

tip

注意f数组的初始化为-INF,这样可以解决上一行不选的问题
循环的边界需要注意

//这里写代码片#include<cstdio>#include<cstring>#include<iostream>using namespace std;int a[51][51],sum[51][51];int n,m;int f[1500][51][51];              void doit(){    memset(f,128,sizeof(f));                //注意初始值     f[1][1][1]=sum[1][1];  f[0][1][0]=0;    int i,j,k,c;    for (i=1;i<n;i++)        for (j=0;j<=i;j++)                  //上一行可以一块都不选             for (k=0;k<=j+1;k++)                for (c=0;c<=m-k;c++)        //总数不能超过m                 {                    f[c+k][i+1][k]=max(f[c+k][i+1][k],f[c][i][j]+sum[i+1][k]);                }           }int main(){    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++)        for (int j=n;j>=i;j--)            scanf("%d",&a[j][i]);    memset(sum,0,sizeof(sum));    for (int i=1;i<=n;i++)        for (int j=1;j<=i;j++)            sum[i][j]=sum[i][j-1]+a[i][j];    doit();    int ans=0;    for (int i=1;i<=n;i++)        for (int j=1;j<=i;j++)            ans=max(ans,f[m][i][j]);    printf("%d\n",ans);    return 0;} 

luogu上的最优解是坐在后面的澍神(yss)不久之前创造的

思路是一样的,优化在于滚动数组以及清奇的码风

//这里写代码片#include<bits/stdc++.h>#define R(i) i = read()#define FUCK cout << "FUCK\n"#define D(i) cout << i << endlusing namespace std;const int N = 55;int f[2][N][N * N / 2], g[2][N][N * N / 2], a[N][N], sum[N][N];inline int read() {    int x = 0; char c = getchar();    while(c < 48 || c > 57) c = getchar();    while(c >= 48 && c <= 57) x = x * 10 + c - 48, c = getchar();    return x;} inline int max(int x, int y) {    return x > y ? x : y;}int main() {//    freopen("1.in", "r", stdin);    int n, m, ans = 0;    R(n), R(m);    for(int i = 1; i <= n; i++)        for(int j = 1; j <= n + 1 - i; j++)            R(a[i][j]);    for(int j = 1; j <= n; j++)        for(int i = 1; i <= n + 1 - j; i++)            sum[i][j] = sum[i - 1][j] + a[i][j];    g[(n + 1) & 1][0][0] = 0;    int t;    for(int j = n; j >= 1; j--) {        t = j & 1;        memset(f[t], 0, sizeof(f[t]));        memset(g[t], -127 / 3, sizeof(g[t]));        for(int i = 0; i <= n + 1 - j; i++) {            int lim = (n - j) * (n + 1 - j) / 2 + i;            for(int k = i; k <= min(lim, m); k++)                f[t][i][k] = max(f[t][i][k], g[t ^ 1][(i - 1) < 0 ? 0 : i - 1][k - i] + sum[i][j]), ans = max(ans, f[t][i][k]);        }        for(int i = n + 1 - j; i >= 0; i--) {            int lim = (n - j) * (n + 1 - j) / 2 + n + 1 - j;            for(int k = i; k <= min(lim, m); k++)                g[t][i][k] = max(f[t][i][k], g[t][i + 1][k]);        }    }    cout << ans;    return 0;}
原创粉丝点击