分析一下得知是DP问题,时间复杂度符合,设dp[i][j]为从第i天开始,第j个位置能得到的最大值,其有三种转移状态
1.与上一天的选择有重合
2.与上一天的选择没有重合,且上一天的选择在左边
3.与上一天的选择没有重合,且上一天的选择在右边
那么得到转移方程,
1.dp[i][j] = max(dp[i-1][x])(x+k-1与j+k-1有交点)-重复部分
2.dp[i][j] = max(dp[i-1][x])(x+k-1在j的左边)+该次选择得到的数
3.同2
我们预处理前缀和,再在每次dp更新后为下一天的dp更新每个点左边的最大值,复杂度为O(nmk)
#include<bits/stdc++.h> using namespace std; #define lowbit(x) ((x)&(-x)) typedef long long LL; const int maxn = 55; const int maxm = 2e4+5; int dp[maxn][maxm], C[maxn][maxm], lmax[maxn][maxm], rmax[maxn][maxm]; int getsum(int i, int p1, int p2) { return C[i][p2] - C[i][p1-1]; } void run_case() { int n, m, k, val; cin >> n >> m >> k; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) { cin >> val; C[i][j] = C[i][j-1] + val; } for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m - k + 1; ++j) { val = getsum(i, j, j+k-1) + getsum(i+1, j, j+k-1); if(i == 1) { dp[i][j] = val; continue; } int mx = 0; // intersect part for(int l = max(1, j-k+1); l <= min(m-k+1, j+k-1); ++l) mx = max(mx, dp[i-1][l]+val-getsum(i, max(l, j), min(j+k-1,l+k-1))); dp[i][j] = mx; if(j > k) dp[i][j] = max(dp[i][j], lmax[i-1][j-k]+val); if(j + k <= m - k + 1) dp[i][j] = max(dp[i][j], rmax[i-1][j+k]+val); } for(int j = 1; j <= m-k+1; ++j) lmax[i][j] = max(dp[i][j], lmax[i][j-1]); for(int j = m-k+1; j >= 1; --j) rmax[i][j] = max(dp[i][j], rmax[i][j+1]); } cout << *max_element(dp[n]+1, dp[n]+1+m) << " "; } int main() { ios::sync_with_stdio(false), cin.tie(0); //cout.setf(ios_base::showpoint);cout.precision(10); //int t; cin >> t; //while(t--) run_case(); cout.flush(); return 0; }
hard vision就是将k的范围扩大很多,可以用线段树O(nmlogm)或者是单调队列O(nm)来优化区间最值问题,以后再补