题意:给定一个 n * m 的 0 1 矩阵,你需要使用以下四种操作使得个矩阵全为 0 ,求出最小的代价。
- 代价为 1 的第一种操作:反转矩阵的某个左上子矩阵。
- 代价为 2 的第二种操作:反转矩阵的某个左下子矩阵。
- 代价为 4 的第三种操作:反转矩阵的某个右上子矩阵。
- 代价为 3 的第四种操作:反转矩阵的某个右下子矩阵。
分析:
- 首先需要发现第二种和第三种操作是可以被第一种操作替代的,且代价不会更高。
- 将原先的矩阵做差分。原先对关于(x,y)的子矩阵的操作,对于差分后的矩阵,第一种操作等价于反转(x,y)一个单元,第四种操作相当于反转(x-1,y-1),(x-1,m),(n,y-1),(n,m)这四个单元。
- 考虑第四种操作什么时候是有意义的,显现需要这个操作使得四个单元都由 0 变成 1 。又由于它们都是关于(n,m)的,所以该操作最多执行一次。

#include<bits/stdc++.h> #define ll long long #define ls u<<1 #define rs u<<1|1 #define mm(x) memset(x,0,sizeof(x)) #define debug(x) cout << #x << ":" << x << ' ' using namespace std; int read() { int a=0;int f=0;char p=getchar(); while(!isdigit(p)){f|=p=='-';p=getchar();} while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();} return f?-a:a; } const int INF=998244353; int T; int n,m; int ans; char s[550][550]; bool val[550][550]; int main() { n=read(); m=read(); for(int i=1;i<=n;++i) scanf("%s",s[i]+1); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) val[i][j]=s[i][j]=='B'; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) val[i][j]^=val[i+1][j]^val[i][j+1]^val[i+1][j+1]; ans=val[n][m]; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(i==n&&j==m) continue; if(val[i][j]) { ans++; if(i==n) continue; if(j==m) continue; if(val[i][m]&&val[n][j]&&val[n][m]) { ans--; val[n][m]=false; } } } printf("%d",ans); return 0; }
题意:给定一个 n * m 的 0 1 矩阵,你需要使用以下四种操作使得个矩阵全为 0 ,求出最小的代价。
- 代价为 1 的第一种操作:反转矩阵的某个左上子矩阵。
- 代价为 2 的第二种操作:反转矩阵的某个左下子矩阵。
- 代价为 4 的第三种操作:反转矩阵的某个右上子矩阵。
- 代价为 2 的第四种操作:反转矩阵的某个右下子矩阵。
分析:相比于前一题,第四种操作的代价变小了。造成的结果是,第四种操作能执行多次。
- 依旧是首先将原先的矩阵做差分。
- 考虑第四种操作作用的点。假设对于同一行或者同一列的两个单元执行第四种操作,那么我们相当于花费了 4 的代价反转了 4 个单元。并不比第一种操作更加节约,因此我们避免这种操作。
- 继续考虑第四种操作,如果个操作影响的四个单元中为 1 的个数少于或者等于三个,考虑到要把至少一个单元反转回来,显然也不会更节约。
- 由于(n,m)是所有第四种操作都会影响的单元,我们暂时先不考虑它。我们考虑是否要对一个单元(x,y)执行第四种操作时,首先保证(x,y),(x,m),(n,y)都为 1 ,将它加入备选队列。
- 接下来我们需要的是在备选队列中选出尽可能多的点,满足没有任意两个点的 X 或者 Y 相等(这是一个经典的关于网格的二分图匹配问题)。

#include<bits/stdc++.h> #define ll long long #define ls u<<1 #define rs u<<1|1 #define mm(x) memset(x,0,sizeof(x)) #define debug(x) cout << #x << ":" << x << ' ' using namespace std; int read() { int a=0;int f=0;char p=getchar(); while(!isdigit(p)){f|=p=='-';p=getchar();} while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();} return f?-a:a; } const int INF=998244353; int T; int n,m; int ans,tot; char s[550][550]; bool val[550][550]; bool link[550][550]; int mat[550]; int vis[550]; bool dfs(int u,int t) { for(int i=1;i<=m;++i) { if(link[u][i]&&vis[i]!=t) { vis[i]=t; if(!mat[i]||dfs(mat[i],t)) { mat[i]=u; return true; } } } return false; } int main() { n=read(); m=read(); for(int i=1;i<=n;++i) scanf("%s",s[i]+1); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) val[i][j]=s[i][j]=='B'; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { val[i][j]^=val[i+1][j]^val[i][j+1]^val[i+1][j+1]; ans+=val[i][j]; } for(int i=1;i<n;++i) for(int j=1;j<m;++j) if(val[i][j]&&val[i][m]&&val[n][j]) link[i][j]=true; for(int i=1;i<=n;++i) if(dfs(i,i)) ++tot; ans-=val[n][m]; ans-=tot; if((tot^val[n][m])&1) ans++; printf("%d",ans); return 0; }