【题解】utpc2021_g Matrix Compressor
utpc2021_g Matrix Compressor
题意
给定 \(n\) 行 \(m\) 列的矩阵 \(a\)。
你需要进行如下操作直到矩阵 \(a\) 大小变为 \(1\times 1\)。
-
合并相邻的两行,贡献是参与合并的行内所有数字之和。
-
合并相邻的两列,贡献是参与合并的列内所有数字之和。
请求出能得到的最大贡献。
\(n,m\le 3\times 10^3\)。
题解
知识点:动态规划。
手玩几组数据,容易发现行和列的合并是独立的,列的合并,不会影响某一行的数的总和,反过来同理。
所以可以预处理出每一行和列的所有数的总和。
那么就变成了经典问题石子合并,不过这里求的是最大贡献。
行和列是一样的,这里只讨论行,设第 \(1\sim i\) 行所有数的为 \(s_i\)。
设 \(dp_{l,r}\) 为合并第 \(l\sim r\) 行后产生的最大贡献。
则有转移:
\[dp_{l,r}=\max(dp_{l+1,r},dp_{l,r-1}) + h_r-h_{l-1}
\]
为什么不用枚举分割点,因为求的是最大的贡献,一个数一个数的合并会使合并次数更多,而上式的本质就是给每个数决定它贡献的次数,同时假设合并 \([l,k],[k+1,r]\),且前者区间长度小于后者长度,则合并 \([l,k-1],[k,r]\) 更优,因为区间长度更长合并次数更多,贡献次数更多,那么最终转移的区间只能是两个极端点,也就是 \([l+1,r],[l,l]\) 或 \([l,r-1],[r,r]\)。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 3005
#define int long long
int n,m,a[N][N],hs[N],ls[N],f[N][N],g[N][N];
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,n){
rep(j,1,m){
char c;
cin>>c;
a[i][j]=c-'0';
hs[i]+=a[i][j];
ls[j]+=a[i][j];
}
}
rep(i,1,n){
rep(j,1,n){
f[i][j]=-1e18;
}
}
rep(i,1,m){
rep(j,1,m){
g[i][j]=-1e18;
}
}
rep(i,1,n){
hs[i]+=hs[i-1];
f[i][i]=0;
}
rep(i,1,m){
ls[i]+=ls[i-1];
g[i][i]=0;
}
rep(len,2,n){
rep(l,1,n){
int r=l+len-1;
if(r>n){
break;
}
f[l][r]=max(f[l+1][r],f[l][r-1])+hs[r]-hs[l-1];
}
}
rep(len,2,m){
rep(l,1,m){
int r=l+len-1;
if(r>m){
break;
}
g[l][r]=max(g[l+1][r],g[l][r-1])+ls[r]-ls[l-1];
}
}
cout<<f[1][n]+g[1][m];
return 0;
}

浙公网安备 33010602011771号