结论:一个合法的分割方案必然导致矩形(a,b)可以被一条直线分成两半,且不存在一个内部小矩形的边界是跨过这条直线的。
这是为什么呢?因为每个小矩形的边界必然是从大矩形的边界中出来的,而如果我们阻止它横穿矩形,就必须要启用一条新的小边。而且这些小边是不能互相阻止的,因为假如互相阻止,那么就形成了一个环,这显然是非法的。所以必然有一条边会横穿大矩形。
——来自鸟哥的讲解
所以我们就可以考虑dp了,设f(n,m,l,r,u,d)表示n*m的矩形其左、右、上、下边界是否还在,然后枚举把它劈成哪两半。注意对称情况记成一次,即约定n≤m,l≤r,u≤d。
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
int f[305][305][2][2][2][2];
LL k;
int query(int n,int m,int l,int r,int u,int d){
if(n>m){
swap(n,m);
swap(l,u),swap(r,d);
}
if(l>r)swap(l,r);
if(d>u)swap(u,d);
if(f[n][m][l][r][u][d]==-1){
f[n][m][l][r][u][d]=min(0x7fffffffLL,(n*m-k)*(n*m-k));
if(u||d||l&&r)
for(int y=1;y<m;++y)
f[n][m][l][r][u][d]=min(f[n][m][l][r][u][d],query(n,y,l,0,u,d)+query(n,m-y,0,r,u,d));
if(l||r||u&&d)
for(int x=1;x<n;++x)
f[n][m][l][r][u][d]=min(f[n][m][l][r][u][d],query(x,m,l,r,0,d)+query(n-x,m,l,r,u,0));
}
return f[n][m][l][r][u][d];
}
#include<cstring>
int main(){
//freopen("bzoj_3810.out","w",stdout);
freopen("bzoj_3810.in","r",stdin);
int n,m;
cin>>n>>m>>k;
memset(f,-1,sizeof(f));
cout<<query(n,m,1,1,1,1);
/*for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int l=2;l--;)
for(int r=2;r--;)
for(int u=2;u--;)
for(int d=2;d--;)
if(f[n][m][l][r][u][d]>1e9)
puts("!");*/
}
总结:
考虑矩形问题时,可以从其能否分治来考虑。