EOJ Monthly 2020.7 C - OLED

本文探讨了EOJMonthly2020.7C-OLED题目的解题思路,通过分析屏保图案在不同位置对屏幕像素点的影响,采用二维数组前缀和的方法高效计算各像素点被点亮的概率,最终输出屏幕亮度分布。

EOJ Monthly 2020.7 C - OLED

题目链接

题意

这道题很有意思,我前几天看一篇关于macOS的时钟屏保的文章,里面就提到过以前的老式显示器会遇到烧屏问题。如果你不太懂,想想以前XP的默认屏保,一个Windows的图标在屏幕上随机飞你就知道了。

在这道题中,为了简化问题,我们将屏幕视为点阵,把屏保图案也当作是个矩形点阵,并认为屏保图案会等概率出现在屏幕上的所有合法位置。

值得一提的是屏幕点阵范围正好是4K UHD标准的分辨率。

思路

由于屏保图案在各个位置等概率出现,那么每个像素点被点亮的概率只与其被所有位置的屏保图案电量的次数有关。大致想象一下大屏幕下小屏保图案覆盖屏幕的画面,会发现,屏幕中间的像素点能够被其四周的全部屏保图案覆盖,亦即屏保图案的每个像素点都有机会使屏幕上中间的像素点点亮;而屏幕边缘的像素点并不能够被四周的全部屏保图案覆盖,原因是受限于屏幕范围,有些屏保图案并不能完整出现,因为屏保图案必须完全显示在屏幕范围内。比如我们考虑屏幕左边缘的一个像素点,显然不存在屏保图案的右半部分能将该点点亮的情况,因为如果能点亮,说明屏保图案的右部某点与屏幕边缘的这一点位置相同,那么就意味着屏保图案的左部会在屏幕之外,这种情况是不允许出现的。如果不太理解,可以参见下图。

在这里插入图片描述

在本图中,如果我们考虑 2 ∗ 3 2*3 23屏保图案在红色五角星这点的点亮情况,显然,只有蓝色的两种位置能够覆盖到这一点,而黑色的四种情况则不成立。我们转换角度,思考像素点可能被屏保图案的哪些部分点亮,则如箭头所指右图示意,该点只会被左边一列所点亮,而这个原因是,该像素点左侧有两列屏保图案落位不成立,所以对应到右边这种视角则是屏保图案右侧两列无法点亮该像素点。

这一研究对于上下右其他三个方向均有效,于是我们可以总结出相应规律,具体规律参考代码部分。

又由于图案的某些部分是不亮的,所以我们需要统计屏保图案可以覆盖某个像素点的覆盖区域内有多少亮着的点,由于屏保图案可能很大,遍历查找显然费时,这里我们可以利用二维数组前缀和来统计某个矩形区域内 1 1 1的个数(预先求出屏保图案的前缀和)。

对于二维数组前缀和的内容,可以参考Caution_X的博客二维数组前缀和

值得一提的是,如果屏保图案都是 1 1 1,那么可以直接预制每个屏保图案位置的影响端点,直接计算屏幕的前缀和差分即可。

代码

//
// Created by Visors on 2020/7/17.
//

#include <iostream>
#include <algorithm>

using namespace std;
const int A = 3845, B = 2165;
int ans[A][B], preSum[A][B];
bool G[A][B];
int n, m, a, b;

int main() {
    cin >> n >> m >> a >> b;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            cin >> G[i][j];
            preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + G[i][j]; // 二维数组前缀和
        }
    int Max = 0;
    for (int i = 1; i <= a; i++) {
        for (int j = 1; j <= b; j++) {
            int left = j, right = b - j + 1, top = i, bottom = a - i + 1;
            int x1, x2, y1, y2;
            if (bottom >= n) x1 = 1;
            else x1 = n - bottom + 1;
            if (right >= m) y1 = 1;
            else y1 = m - right + 1;
            x2 = min(n, top);
            y2 = min(m, left);
            ans[i][j] = preSum[x2][y2] - preSum[x1 - 1][y2] - preSum[x2][y1 - 1] + preSum[x1 - 1][y1 - 1];
            Max = max(Max, ans[i][j]);
        }
    }
    for (int i = 1; i <= a; i++) {
        for (int j = 1; j < b; j++) cout << int(((double) ans[i][j] / Max) * 100) << ' ';
        cout << int(((double) ans[i][b] / Max) * 100) << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值