OpenJudge NOI 1.11 03:矩形分割

【题目链接】

OpenJudge NOI 1.11 03:矩形分割

【题目考点】

1. 二分答案

【解题思路】

该题为二分答案问题。
原题中:“使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。”这句话指明了二分答案的满足条件。
很明显,对于竖线 x = k x=k x=k,在直线左侧小矩形面积大于等于右侧小矩形面积的前提下,k越小,两边面积之差越小。
因此,求的是满足某一条件的k的最小值。而要满足的条件正是直线 x = k x=k x=k左侧小矩形的面积大于等于右侧小矩形的面积
总结下来,该题需要先通过二分答案求:满足直线 x = k x=k x=k左侧小矩形的面积大于等于右侧小矩形的面积的前提下,k的最小值。
下一个要求为“使得大矩形在直线左边的的面积尽可能大”,该要求的优先级低于“ x = k x=k x=k左右两侧小矩形面积差值最小”。
因此在保证当前 x = k x=k x=k左右两侧小矩形面积差值不变(亦即保持左侧面积不变)的情况下,k从小向大遍历,取到可能取到的最大值。
最后输出k,即为结果。

【题解代码】

解法1:二分答案
#include <bits/stdc++.h>
using namespace std;
#define N 10005
struct Rect//矩形 
{
    int l, t, w, h;//(l,t):矩形左上角坐标 w:宽 h:高 
};
Rect rec[N];
int rx, n;//rx:题目中的R
long long totArea;//总面积 
long long getLeftArea(int k)//x=k左侧所有小矩形面积加和
{
    long long leftArea = 0, lw;//lw:竖线x=k左侧矩形的宽 
    for(int i = 1; i <= n; ++i)
    {
        if(rec[i].l+rec[i].w <= k)//矩形在x=k左边
            lw = rec[i].w;
        else if(rec[i].l <= k)//x=k穿过矩形
            lw = k-rec[i].l;
        else//矩形在x=k右侧
            lw = 0;
        leftArea += lw*rec[i].h;
    }
    return leftArea;
}
bool check(int k)//选择x=k这条竖线是否满足左侧面积大于等于右侧面积
{
    long long leftArea = getLeftArea(k);
    long long rightArea = totArea - leftArea;
    return leftArea >= rightArea;
}
int main()
{
    cin >> rx >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> rec[i].l >> rec[i].t >> rec[i].w >> rec[i].h;
        totArea += (long long)rec[i].w*rec[i].h;//求这个小矩形的面积,加和(注意,w,h都是int,而乘积会超过int的范围,要转为long long) 
    }
    int l = 0, r = rx, mid, k;
    while(l < r)//二分答案求满足条件的最小值 
    {
        mid = (l+r)/2;
        if(check(mid))//条件为:选择x=k这条竖线左侧面积大于等于右侧面积
            r = mid;
        else
            l = mid + 1;
    }
    k = l;//取到:满足x=k这条竖线左侧面积大于等于右侧面积的k的最小值 
    int leftArea = getLeftArea(k);//此时x=k左侧小矩形面积 
    while(getLeftArea(k+1) == leftArea && k+1 <= rx)//保持左侧面积不变(也就是保持左右侧面积差值不变),k从小向大遍历,取到可能取到的最大值
        k++;
    cout << k;
    return 0;
}
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 这是一个关于矩形分割的问题。根据某些方面的需求,我们要把一个n×m的木板切成一个个1×1的小方块。对于一个块木板,我们只能从某条横线或者某条竖线(要在格线上)下去切割,而且要保证切割后得到的木板不是不平均的,从不同的线切下去的代价也不同。 ### 回答2: 我们希望使得最终的所有小方块的代价之和最小。这个问题可以用动态规划算法来解决。 首先考虑木板的横向切割。定义一个二维数组dp[i][j]表示将从第i行到第j行的部分木板切成小方块的最小代价。当i=j时,说明只有一行,无需切割,代价为0。当i<j时,假设我们选择在第k行进行切割,则最小代价为dp[i][k]+dp[k+1][j]+第i到第j行的木板的代价之和。为了求出每一行到每一行的木板代价之和,我们可以在预处理时用一个前缀和sum[i][j]表示从第1行到第i行,从第j列到第m列的木板代价之和。具体地,sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+第i行第j列的木板代价。因此,第i到第j行的木板代价之和为sum[j][m]-sum[i-1][m]-sum[j][0]+sum[i-1][0]。最后的答案即为dp[1][n]。 接下来考虑竖向切割。同样地,定义一个二维数组dp[i][j]表示将从第i列到第j列的部分木板切成小方块的最小代价。当i=j时,代价为0。当i<j时,假设我们选择在第k列进行切割,则最小代价为dp[i][k]+dp[k+1][j]+第i到第j列的木板的代价之和。为了求出每一列到每一列的木板代价之和,可以先将整个木板逆时针旋转90度,然后按照之前的方法进行计算,最后再将答案逆时针旋转90度。 总的时间复杂度为O(n^3),可以通过本题。 ### 回答3: 我们的目标是最小化这个代价,使得木板被分割成1×1的小方块。这种问题就叫做矩形分割矩形分割问题实质上就是一道动态规划问题。我们可以先定义一个二维数组f[i, j],表示将i列j行的矩形分割成1×1的小方块所需要的最小代价。同时,我们还需要定义一个代价数组c[i, j],表示从第i条竖线或第j条横线切割下去所需要的代价。 根据问题描述,我们可以得出f[i, j]的转移方程如下: f[i, j] = min{f[i-1, j]+c[i, j], f[i, j-1]+c[i, j]} 其中,f[i-1, j]表示将i-1列j行的矩形分割成小方块后所需要的最小代价,f[i, j-1]表示将i列j-1行的矩形分割成小方块后所需要的最小代价,c[i, j]表示从第i条竖线或第j条横线切割下去所需要的代价。因此,f[i, j]就是这两个值中的较小值加上c[i, j]。 最终,我们就可以得出将整块n×m的矩形分割成1×1小方块的最小代价,即f[n, m]。这个问题虽然看起来比较简单,但是要写出正确的代码还是有一定难度的。因此,需要我们多加练习,积累经验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值