1. 程式人生 > 實用技巧 >每日一題 NC20242 [SCOI2005]最大子矩陣(動態規劃 dp)

每日一題 NC20242 [SCOI2005]最大子矩陣(動態規劃 dp)

https://ac.nowcoder.com/acm/problem/20242

這裡有一個n*m的矩陣,請你選出其中k個子矩陣,使得這個k個子矩陣分值之和最大。

注意:選出的k個子矩陣 不能相互重疊。

輸入描述:

第一行為n,m,k(1 ≤ n ≤ 100,1 ≤ m ≤ 2,1 ≤ k ≤ 10),
接下來n行描述矩陣每行中的每個元素的分值(每個元素的分值的絕對值不超過32767)。

輸出描述:

只有一行為k個子矩陣分值之和最大為多少。
示例1

輸入

3 2 2
1 -3
2 3
-2 3

輸出

9

題解:

因為m為1或者2 分類討論
先預處理每列的字首和,這樣兩個相減即為矩陣值


m==1時 dp[i][k] 表示到第i行選j個子矩陣的最大值
每次到下一行有兩種方案
不選 dp[i][k]=dp[i-1][k]
選 for(j=0;j<i;j++) dp[i][k]=max(dp[i][k], dp[j][k-1] + a[i]-a[j])
時間複雜度 O(n^2*k)
下面討論m==2  f[i][j][k] 表示 第一列到i行  第二列到j行 選k個子矩陣的最大值
不選 f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k])

當僅選取第一列的某段區間時:
f[i][j][k]=max(f[l][j][k-1]+sum[i][1]-sum[l-1][1]) 1<=l<i


當僅選取第二列的某段區間時:
f[i][j][k]=max(f[i][l][k-1]+sum[j][2]-sum[l-1][2]) 1<=l<j


當i==j時,可以選取兩列一起的
f[i][j][k]=max(f[l][l][k]+sum[i][1]+sum[i][2]-sum[l-1][1]-sum[l-1][2])
最後所有情況取max 就是答案
時間複雜度 O(n^3*k)


#include <bits/stdc++.h>
using namespace std;
const int maxn=107;
int a[maxn],b[maxn],dp[maxn][maxn],f[maxn][maxn][maxn];
int main(){ int n,m,x,y,p; scanf("%d%d%d",&n,&m,&p); if(m==1){ for(int i=1;i<=n;i++){ scanf("%d",&x); a[i]=a[i-1]+x; } for(int k=1;k<=p;k++){ for(int i=1;i<=n;i++){ dp[i][k]=dp[i-1][k]; for(int j=0;j<i;j++){ dp[i][k]=max( dp[i][k], dp[j][k-1]+a[i]-a[j] ); } } } printf("%d\n",dp[n][p]); }else{ for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); a[i]=a[i-1]+x; b[i]=b[i-1]+y; } for(int k=1;k<=p;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ f[i][j][k]=max( f[i-1][j][k],f[i][j-1][k] ); for(int l=0;l<i;l++){ f[i][j][k]=max( f[i][j][k], f[l][j][k-1] +a[i]-a[l] ); } for(int l=0;l<j;l++){ f[i][j][k]=max( f[i][j][k],f[i][l][k-1]+b[j]-b[l] ); } if(i==j) for(int l=0;l<i;l++){ f[i][j][k]=max( f[i][j][k],f[l][l][k-1]+a[i]-a[l]+b[i]-b[l] ); } } } } printf("%d\n",f[n][n][p]); } return 0; } /* 3 2 2 1 9 -2 -1 3 6 */
View Code