传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3810
思路:这是一道很有趣的题目。
我一开始以为是费用流相关然而并不会建图,后来感觉不会做就各种猜结论,碰运气才猜对了,其实还是水平不足。
存在一个结论:一个满足条件的矩形必然存在一条横贯上下或者左右的线。
为什么呢?用反证法,考虑一个矩形,边界上有一坨点,向内引入平行于边界的线,没有一个能横穿,主要分两种情况讨论,通过画图可以得知其中一种是楼梯状的下降体但最后一定会存在一条贯穿的线,另外一种情况一旦出现就会导致出现不和法的矩形,因此结论成立。
因此就可以
dp
了,设计状态
f[i][j][S]
表示长i宽j且上下左右的边界状态为二进制数
S
的最小值(
另外我要吐槽一下这是个卡常题,我
T
<script type="math/tex" id="MathJax-Element-7">T</script>了一发,最后把矩形旋转4次同时得到等价的几个才过掉。
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define N 300
using namespace std;
typedef long long LL;
LL n,m,k,f[16][N + 5][N + 5];
inline LL solve(LL a,LL b,LL S){
LL x,y;
if (f[S][a][b] >= 0) return f[S][a][b];
f[S][a][b] = (a * b - k) * (a * b - k);
if (S & 3LL)
for (int i = 1;i < b; ++i){
x = solve(a,i,S - (S & 8LL));
y = solve(a,b - i,S - (S & 4LL));
if (x + y < f[S][a][b]) f[S][a][b] = x + y;
}
if (S & 12LL)
for (int i = 1;i < a; ++i){
x = solve(i,b,S - (S & 2LL));
y = solve(a - i,b,S - (S & 1LL));
if (x + y < f[S][a][b]) f[S][a][b] = x + y;
}
if (S == 3LL)
for (int i = 1;i < a; ++i){
x = solve(i,b,1LL);
y = solve(a - i,b,2LL);
if (x + y < f[S][a][b]) f[S][a][b] = x + y; }
if (S == 12LL)
for (int i = 1;i < b; ++i){
x = solve(a,i,4LL);
y = solve(a,b - i,8LL);
if (x + y < f[S][a][b]) f[S][a][b] = x + y;
}
//if (S == 0LL) f[S][b][a] = f[S][a][b]
if (S == 1LL) f[2][a][b] = f[4][b][a] = f[8][b][a] = f[S][a][b];
if (S == 2LL) f[1][a][b] = f[4][b][a] = f[8][b][a] = f[S][a][b];
if (S == 3LL) f[12][b][a] = f[S][a][b];
if (S == 4LL) f[8][a][b] = f[1][b][a] = f[2][b][a] = f[S][a][b];
if (S == 5LL) f[10][a][b] = f[9][b][a] = f[6][b][a] = f[S][a][b];
if (S == 6LL) f[9][a][b] = f[10][b][a] = f[5][b][a] = f[S][a][b];
if (S == 7LL) f[11][a][b] = f[13][b][a] = f[14][b][a] = f[S][a][b];
if (S == 8LL) f[4][a][b] = f[2][b][a] = f[1][b][a] = f[S][a][b];
if (S == 9LL) f[6][a][b] = f[10][b][a] = f[5][b][a] = f[S][a][b];
if (S == 10LL) f[5][a][b] = f[9][b][a] = f[6][b][a] = f[S][a][b];
if (S == 11LL) f[7][a][b] = f[13][b][a] = f[14][b][a] = f[S][a][b];
if (S == 12LL) f[3][b][a] = f[S][a][b];
if (S == 13LL) f[14][a][b] = f[7][b][a] = f[11][b][a] = f[S][a][b];
if (S == 14LL) f[13][a][b] = f[7][b][a] = f[11][b][a] = f[S][a][b];
if (S == 15LL) f[S][b][a] = f[S][a][b];
return f[S][a][b];
}
int main(){
memset(f,-1,sizeof(f));
cin>>n>>m>>k;
cout<<solve(n,m,15LL);
return 0;
}
注意情况:1.转移的时候一定要考虑周全
2.简化代码提高代码能力
3.当不会做的时候大胆猜测结论使题目可做是很重要的一种能力