Loading

K-Matrix(dp) 第十八届浙大城市学院程序设计竞赛

Link

对于每行来说,第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);
}

启示

  1. 对于多维的数组 可以memset(dp[0],-1,sizeof dp[0]);
  2. dp数组的初始化可以通过dp[1]这一层的情况来看 观察dp[1]时合法的状态有哪些 例如这 题所有的dp[1][j]k是符合条件的 因此需要把mx[0][0][k]都初始化为0
  3. 滚动数组 每个i 每次cur=i&1 这样cur就是1010这样循环 取到上一层只需要cur^1即 可
  4. i,j,k三层循环的顺序是需要斟酌的,例如这题当i和k确定下来,那么只有j>=i*k的才可能合法
    因此把i,k放在外层循环,j放在最内层循环,这样就可以降低复杂度
  5. mx数组的转移这行尤其重要 需要注意的是 哪怕当前的状态不合法,那么mx数组也是需要转移的,因为可能之前的状态是合法的.这点很重要 把mx数组的转移移到if语句中就是错的
  6. 对dp数组和mx数组的memset也是需要注意的
posted @ 2021-03-25 22:10  金木换  阅读(119)  评论(0)    收藏  举报