传送门
没有什么技巧,就是记忆化搜索
每次枚举是横着切割还是竖着切割
且定义 f [ l 1 ] [ r 1 ] [ l 2 ] [ r 2 ] f[l1][r1][l2][r2] f[l1][r1][l2][r2]为左上角 [ l 1 , r 1 ] [l1,r1] [l1,r1]右下角 [ r 1 , r 2 ] [r1,r2] [r1,r2]的矩阵的
最优切割方式
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 29;
int n,m,p,casenum,pre[22][22],f[22][22][22][22];
int get(int l1,int r1,int l2,int r2)
{
return pre[l2][r2]+pre[l1-1][r1-1]-pre[l2][r1-1]-pre[l1-1][r2];
}
int dfs(int l1,int r1,int l2,int r2)
{
int &s = f[l1][r1][l2][r2];
if( s!=-1 ) return s;
if( get(l1,r1,l2,r2)<=1 ) return s=0;
int ans = 1e9;
for(int i=l1;i<l2;i++)
ans = min( ans,dfs(l1,r1,i,r2)+dfs(i+1,r1,l2,r2)+r2-r1+1 );//横着切
for(int i=r1;i<r2;i++)
ans = min( ans,dfs(l1,r1,l2,i)+dfs(l1,i+1,l2,r2)+l2-l1+1 );
return s = ans;
}
int main()
{
while( cin >> n >> m >> p )
{
memset( f,-1,sizeof(f) );
for(int i=1;i<=p;i++)
{
int l,r; scanf("%d%d",&l,&r);
pre[l][r] = 1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
pre[i][j] += pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1];
printf("Case %d: ",++casenum);
cout << dfs(1,1,n,m) << '\n';
memset( pre,0,sizeof(pre) );
}
}