K-Matrix(dp) 第十八届浙大城市学院程序设计竞赛
对于每行来说,第i行选择的格子的数量一定要小于等于上一行,由此很容易想到dp
dp[i][j][k]: 前i行选j个且第i行选k个的最大贡献
\(\max \limits_{x}\{dp[i-1][j-k][x>=k]\} + s[i][k]\) (s[i][k]是第i行前k个的和)
这题打开了我个人的dp新世界 这题的知识点包括了 滚动数组 和对于x>=k这种情况的优化
细节很多 我会写在后面的启示里
这题最关键的是对转移方程中x>=k怎么处理 最容易想到的方法是再来一层循环 但是这样的复杂度过高 首先肯定i,j,k是一定要写入循环的,观察转移方程,在i固定的情况下,当j-k的值相同时,我们希望这次的k比上一次要小,这样就可以维护后缀最大值,即用mx[i][j][k]来表示max(dp[i][j][x])其中x>=k
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
const int N = 305;
int n,m,g;
int dp[2][1005][N],a[N][N],mx[2][1005][N],s[N][N];
signed main()
{
//ios::sync_with_stdio(false);
scanf("%lld %lld %lld",&n,&m,&g);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%lld",&a[i][j]);
s[i][j] = s[i][j-1]+a[i][j];
}
}
memset(mx[0],-1,sizeof mx[0]);
for(int k=min(m,g);k>=1;k--) mx[0][0][k]=0;
int ans=0;
for(int i=1;i<=n;i++)
{
int cur = i&1;
memset(mx[cur],-1,sizeof mx[cur]);
memset(dp[cur],-1,sizeof dp[cur]);
for(int k=min(m,g);k>=1;k--)
{
for(int j=i*k;j<=g&&j<=i*m;j++)
{
if(mx[cur^1][j-k][k]!=-1)
{
dp[cur][j][k] = mx[cur^1][j-k][k] + s[i][k];
if(j==g) ans = max(ans,dp[cur][j][k]);
}
mx[cur][j][k] = max(mx[cur][j][k+1],dp[cur][j][k]);//这行一定要写在if语句的外面 即哪怕当前状态不合法 mx也要转移
}
}
}
printf("%lld",ans);
}
启示
- 对于多维的数组 可以memset(dp[0],-1,sizeof dp[0]);
- dp数组的初始化可以通过dp[1]这一层的情况来看 观察dp[1]时合法的状态有哪些 例如这 题所有的dp[1][j]k是符合条件的 因此需要把mx[0][0][k]都初始化为0
- 滚动数组 每个i 每次cur=i&1 这样cur就是1010这样循环 取到上一层只需要cur^1即 可
- i,j,k三层循环的顺序是需要斟酌的,例如这题当i和k确定下来,那么只有j>=i*k的才可能合法
因此把i,k放在外层循环,j放在最内层循环,这样就可以降低复杂度 - mx数组的转移这行尤其重要 需要注意的是 哪怕当前的状态不合法,那么mx数组也是需要转移的,因为可能之前的状态是合法的.这点很重要 把mx数组的转移移到if语句中就是错的
- 对dp数组和mx数组的memset也是需要注意的

浙公网安备 33010602011771号