导航

CF1592F1 Alice and Recoloring 1

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

题意:

给定一个 \(n \times 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)\) 为左上角,\((x-1,y)\) 为右下角的矩形。

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

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

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

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

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

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

可以对原矩阵 \(c\) 进行一个巧妙的转化:令白色格子为 \(0\),黑色格子为 \(1\)。设 \(a_{i,j}\) 代表 \((c_{i,j}+c_{i+1,j}+c_{i,j+1}+c_{i+1,j+1}) \bmod 2\) 的结果。

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

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

对于操作 \(4\),翻转一个以 \((x,y)\) 为左下角,\((n,m)\) 为右下角的矩阵,等价于只翻转 \(a_{x-1,y-1},a_{x-1,m},a_{y-1,n},a_{n,m}\)

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

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

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

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

于是判断一下能否进行操作 \(4\) 并统计一下需要翻转的 \(a_{i,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 2022-01-24 15:17  CHK666  阅读(47)  评论(1编辑  收藏  举报