luoguP1437 [HNOI2004]敲砖块(dp)
题目背景
无
题目描述
在一个凹槽中放置了 n 层砖块、最上面的一层有n 块砖,从上到下每层依次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示。
14 15 4 3 2333 33 76 22 13 1122 2331
如果你想敲掉第 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 5 2 2 3 4 8 2 7 2 3 49输出样例#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
#include
#include 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;ifor (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
#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;
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
