题意: 给定一个倒三角,共行,第
行有
个元素,选取
必须先选取
和
,现在要求选择
个元素,求选取的
个元素可以获得的最大值。
题解:
学习参考了该篇题解:https://blog.nowcoder.net/n/4f95681bfb9e4bf29b49ce9aa321248d
由于选择一个元素,以其作为倒三角的顶点的三角形必须全部选中,所以从给定的倒三角的最右端开始,先处理第列,再处理第
列,...,最后处理第
列。
表示当前已经选择了
这个元素,并且总共选择了
个元素可以获得的最大值。
状态转移方程为
其中是该状态必选的。
状态转移的解释:由于这个状态必然选择了
这个元素,
- 对于第一维,由于第
必然已经被选择了,所以该状态由后一列转移过来。
- 对于第二维,第
列的
就必然被选择,因此
的下限就是
,上限就是第
列的元素数即
- 对于第三维,前一个状态相较于当前状态少了
个元素,因此为
注意:
本题中有一个细节容易被忽略,即枚举行时应该从开始,因为要考虑到第
行的元素转移过来是从后一列的第
行转移过来的。
举个栗子:
,答案是选择
对于每一列保存表示该列元素均不被选择,那么就实现了跨列组合。
代码:
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
const int N = 60, M = 1310;
int f[N][N][M];
int q[N][N];
int cnt[N];
int n, m;
int main()
{
while(~scanf("%d%d", &n, &m)) {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n - i + 1; j++)
q[i][j] = read(), q[i][j] += q[i - 1][j];
m = min(m, n * (n + 1) / 2); //最多n * (n + 1) / 2个元素
memset(f, 0, sizeof f);
for(int i = 1; i <= n; i++) cnt[i] = cnt[i - 1] + i;
for(int j = n; j >= 1; j--)
for(int i = 0; i <= n - j + 1; i++) //!!!!
for(int k = cnt[i]; k <= m; k++)
for(int c = max(i - 1, 0); c <= n - j; c++) //第j+1列最多n-j个元素
f[j][i][k] = max(f[j][i][k], f[j + 1][c][k - i] + q[i][j]);
int res = 0;
for(int j = 1; j <= n; j++)
for(int i = 1; i <= n - j + 1; i++)
res = max(res, f[j][i][m]);
printf("%d\n", res);
}
return 0;
} 
京公网安备 11010502036488号