第十八届浙大城市学院程序设计竞赛(同步赛)K
大意
略。。
思路
什么叫王中王啊。。要是比赛时调出来,直接从100+蹦到20整(
首先我们按行观察矩阵,不难发现每行选择的个数一定是非递降的才能满足题意,也就是每行不能选的比上一行多。
所以, 先设设 (dp[i][j][k]) 表示第 (i) 行 ,本行选了 (j) 个方块,且总共选了 (k) 个方块时的最大值。
因为我们在当前行,如果要选 (r) 个,只需要上一行选择个数大于等于 (r) 即可。
如果直接枚举上一行选择几个,即枚举 (rleq j) 的 (j) 的值显然复杂度过大,所以我们对 (j) 这一维做一下后缀优化,即将 (j) 的定义改为:本行选了大于等于 (j) 个方块。
这样理论复杂度是 (Theta(n*m*min(1000,n*m)))
因为空间会炸(
所以将 (i) 的一维要换成滚动数组。
代码
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define cint const int&
#define Pi acos(-1)
const int mod = 1e9+7;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
ll n, m, kk;
ll d[2][1001][301];
// 第i行总共选了j个方格且第i行选择的方格数大于等于k的最大值
ll sum[301][301];
ll ans;
int main() {
cin >> n >> m >> kk;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++) {
cin >> sum[i][j];
sum[i][j] += sum[i][j-1];
}
for(int i=1, st = 1; i<=n; i++, st ^= 1) {
memset(d[st], -1, sizeof d[st]);
for(int j=kk; j>=0; j--) {
for(int k=m; k>=1; k--) {
if(k != m) d[st][j][k] = max(d[st][j][k], d[st][j][k+1]);
if(j>=k)if(d[st^1][j-k][k] >= 0) {
d[st][j][k] = max(d[st][j][k], d[st^1][j-k][k] + sum[i][k]);
ans = max(ans, d[st][j][k]);
}
}
}
}
cout << ans << endl;
return 0;
}