bzoj 3810: [Coci2015]Stanovi

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3810
思路:这是一道很有趣的题目。
我一开始以为是费用流相关然而并不会建图,后来感觉不会做就各种猜结论,碰运气才猜对了,其实还是水平不足。
存在一个结论:一个满足条件的矩形必然存在一条横贯上下或者左右的线。
为什么呢?用反证法,考虑一个矩形,边界上有一坨点,向内引入平行于边界的线,没有一个能横穿,主要分两种情况讨论,通过画图可以得知其中一种是楼梯状的下降体但最后一定会存在一条贯穿的线,另外一种情况一旦出现就会导致出现不和法的矩形,因此结论成立。
因此就可以 dp 了,设计状态 f[i][j][S] 表示长i宽j且上下左右的边界状态为二进制数 S 的最小值(S 0 表示不是初始边界,1表示是初始边界),在上下左右不全是边界时条件更强更满足以上结论,但这样没法递推,记忆化搜索一波就好了,注意转移的时候不要漏情况。
另外我要吐槽一下这是个卡常题,我 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.当不会做的时候大胆猜测结论使题目可做是很重要的一种能力
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值