导航

统计

CF1592F1 Alice and Recoloring 1

https://www.luogu.com.cn/blog/DreamNOI2022/post-ti-xie-cf1592f1-alice-and-recoloring-1-post
超级妙妙题!

题意:

给定一个 n×m 的矩阵,初始时所有格子都是白色,可以进行以下四种操作:

1、翻转一个以 (1,1) 为左上角的任意子矩阵的所有颜色,代价为 1

2、翻转一个以 (n,1) 为左下角的任意子矩阵的所有颜色,代价为 2

3、翻转一个以 (1,m) 为右上角的任意子矩阵的所有颜色,代价为 4

4、翻转一个以 (n,m) 为右下角的任意子矩阵的所有颜色,代价为 3

求将初始局面变为给定局面花费代价的最小值。

首先只有黑白两种颜色,所以将一个格子翻转偶数次,相当于没有翻转。

其次,将初始局面变为给定局面,等价于将给定局面变为初始局面。因此问题可以转化成给定一个局面,要将这个局面全部变为白色格子所花费的最小代价。

发现这四种操作好像比较对称,考虑进行一些转化。

对于操作 2,翻转一个以 (n,1) 为左下角,(x,y) 为右上角的矩阵,可以转化为使用两次操作 1,第一次翻转以 (1,1) 为左上角,(n,y) 为右下角的矩形,第二次翻转以 (1,1) 为左上角,(x1,y) 为右下角的矩形。

显然这两种操作完全等价,花费的代价也相同。因此,操作 2 没有任何意义。

类似地,对于操作 3,翻转一个以 (1,m) 为右上角,(x,y) 为左下角的矩阵,可以转化为使用两次操作 1,第一次翻转以 (1,1) 为左上角,(x,m) 为右下角的矩形,第二次翻转以 (1,1) 为左上角,(x,y1) 为右下角的矩形。

这两种操作也是完全等价的,但是两次操作 1 只需要花费 2 代价,因此操作 3 也没有意义。

容易知道操作 4 无法转化为使用不超过三次的操作 1,因此操作 4 是有意义的。

于是证明了只有操作 1 和操作 4 有意义。下面考虑如何操作。

显然矩阵修改不好维护且十分麻烦,最好将其转化为单点修改。

可以对原矩阵 c 进行一个巧妙的转化:令白色格子为 0,黑色格子为 1。设 ai,j 代表 (ci,j+ci+1,j+ci,j+1+ci+1,j+1)mod2 的结果。

容易知道,将原局面全部变成白色格子的充要条件为对于任意 1i,jn,ai,j=0。于是问题转化为将所有 ai,j 变为 0 的最小方案。

考虑对于操作 1,翻转一个以 (1,1) 为左上角,(x,y) 为右下角的矩形等价于只翻转 ax,y

对于操作 4,翻转一个以 (x,y) 为左下角,(n,m) 为右下角的矩阵,等价于只翻转 ax1,y1,ax1,m,ay1,n,an,m

这两个结论可以自行画图证明,这里略去。

这样操作 1 就是翻转一个点,代价为 1,操作 4 就是翻转四个点,代价为 3

看起来操作 4 好像更优。但是操作 4 能用必须满足两个条件:1、ax1,y1,ax1,m,ay1,n,an,m 均为 1(翻转 ai,j=0 的点会带来负收益);2、只能进行一次(因为多次进行操作 4 会不断翻转 an,m,实际上最多翻转 3 个有效点)

所以最多进行一次操作 4,剩下的点都用操作 1 完成。

于是判断一下能否进行操作 4 并统计一下需要翻转的 ai,j 的个数即可。

时间复杂度 O(nm)

代码:

#include<bits/stdc++.h>
#define ll long long
#define back return
#define ri register int
#define ull unsigned ll
using namespace std;
ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    back x*f;
}
int n,m,ans,a[505][505],b[505][505];
char c[505][505];
int main()
{
    n=read(),m=read();
    for(ri i=1;i<=n;i++)
        for(ri j=1;j<=m;j++)
        {
            cin>>c[i][j];
            if(c[i][j]=='W')
                b[i][j]=0;
            else
                b[i][j]=1;
        }
    for(ri i=1;i<=n;i++)
        for(ri j=1;j<=m;j++)
            a[i][j]=(b[i][j]+b[i][j+1]+b[i+1][j]+b[i+1][j+1])%2;
    for(ri i=1;i<=n;i++)
        for(ri j=1;j<=m;j++)
            if(a[i][j]==1)  
                ans++;
    if(a[n][m])
        for(ri i=1;i<=n;i++)
            for(ri j=1;j<=m;j++)
                if(a[i-1][j-1]&&a[i-1][m]&&a[n][j-1])   
                {
                    cout<<ans-1<<"\n";
                    back 0;
                }
    cout<<ans<<"\n";
    back 0;
}

posted on   CHK666  阅读(47)  评论(1编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示