P2140 记忆化搜索
Usada_Pekora · · 题解
题意:纵横划分区域,使得除去某一区域的值后其他区域的和不超过
思路:用 dfs 划分区域,如果沿着当前轴划分是可行的,那么再对划分后的区域进行 dfs 。
但是这样做的时间成本是很高的, 因为这种做法对同一区间做了重复的计算,这个时候就考虑用数组记录某一区间的最优值,在产生重复计算时直接调用即可。 设
代码如下。
#include<bits/stdc++.h>
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
typedef pair<int,int> pii;
inline int read(){
int x = 0;char ch = getchar();
while(ch < '0' || ch > '9'){ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x<<1)+(x<<3)+(ch^48);ch = getchar();}
return x;
}
int n,m,u,a[35][35],sum = 0,s[35][35];
pii f[35][35][35][35];
#define calc(x1,y1,x2,y2) (s[x2][y2]+s[x1-1][y1-1]-s[x1-1][y2]-s[x2][y1-1])
inline pii dfs(int x1,int y1,int x2,int y2,int all){
if(f[x1][y1][x2][y2].first) return f[x1][y1][x2][y2];
if(x1 == x2 && y1 == y2) return pii{1,u-(sum-all)};
pii t;t.first = 1,t.second = u-(sum-all);
for(register int i = y1;i < y2;++i){
int s1 = calc(x1,y1,x2,i),s2 = calc(x1,i+1,x2,y2);
if(sum-s1 <= u && sum-s2 <= u){
pii p1 = dfs(x1,y1,x2,i,s1),p2 = dfs(x1,i+1,x2,y2,s2);
if(p1.first+p2.first > t.first)t.first = p1.first+p2.first,t.second = min(p1.second,p2.second);
else if(p1.first+p2.first == t.first) t.second = max(t.second,min(p1.second,p2.second));
}
}
for(register int i = x1;i < x2;++i){
int s1 = calc(x1,y1,i,y2),s2 = calc(i+1,y1,x2,y2);
if(sum-s1 <= u && sum-s2 <= u){
pii p1 = dfs(x1,y1,i,y2,s1),p2 = dfs(i+1,y1,x2,y2,s2);
if(p1.first+p2.first > t.first)t.first = p1.first+p2.first,t.second = min(p1.second,p2.second);
else if(p1.first+p2.first == t.first) t.second = max(t.second,min(p1.second,p2.second));
}
}
return f[x1][y1][x2][y2] = t;
}
int main(){
n = read(),m = read(),u = read();
for(register int i = 1;i <= n;++i)
for(register int j = 1;j <= m;++j) a[i][j] = read(),sum += a[i][j];
for(register int i = 1;i <= n;++i)
for(register int j = 1;j <= m;++j) s[i][j] = s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j];
pii ans = dfs(1,1,n,m,sum);
printf("%d %d",ans.first,ans.second);
return 0;
}