luoguP1437 [HNOI2004]敲砖块(dp)
2017-10-25 19:22
323 查看
题目背景
无题目描述
在一个凹槽中放置了 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 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<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 << endl using namespace std; const int N = 55; int f[2] [N * N / 2], g[2] [N * N / 2], a , sum ; 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; }
相关文章推荐
- [luoguP1437] [HNOI2004]敲砖块(DP)
- [luoguP2285] [HNOI2004]打鼹鼠(DP)
- 【codevs 1257】【NOIP 模拟题】[HNOI 2004] 敲砖块(dp)
- 神奇DP [HNOI2004] 打砖块
- Luogu 1437 [HNOI2004]敲砖块 (动态规划)
- BZOJ 1210 HNOI 2004 邮递员 插头DP
- BZOJ1027 [HNOI2004]打鼹鼠 【dp】
- BZOJ 2004: [Hnoi2010]Bus 公交线路 [DP 状压 矩阵乘法]
- [BZOJ 1207][HNOI2004]打鼹鼠:DP
- bzoj1207:[HNOI2004]打鼹鼠[DP]
- bzoj 1212: [HNOI2004]L语言(AC自动机+DP)
- [luoguP3205] [HNOI2010]CHORUS 合唱队(区间DP)
- bzoj1212 [HNOI2004]L语言(AC+dp)
- 【HNOI2004】【codevs1256】【BZOJ1207】打鼹鼠,小小DP
- 【DP】【HNOI2004】打鼹鼠
- BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]
- bzoj 1207: [HNOI2004]打鼹鼠 (dp)
- [luoguP2280] [HNOI2003]激光炸弹(DP)
- 大视野1212--[HNOI2004]L语言(Trie+DP)
- bzoj1212: [HNOI2004]L语言 Trie+dp