传送门

没有什么技巧,就是记忆化搜索

每次枚举是横着切割还是竖着切割

且定义 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) );
	}
}