欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

题解 CF1391D 【505】

程序员文章站 2024-02-24 12:53:34
...

这道题目的话看似没有头绪,我们分析一下。

偶数长度平方矩阵为:2×22 \times 24×44 \times 48×88 \times 8……

我们考虑是否可能同时满足所有 2×22 \times 2 的矩阵 11 的个数为奇数,所有 4×44 \times 4 的矩阵 11 的个数为奇数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uD8pFpHw-1597048634801)(https://i.loli.net/2020/08/10/Wb7EZy8nuXHwVMl.png)]

如图,每个黑色正方形是一个 2×22 \times 2 的矩阵,为了满足要求它们 11 的个数都为奇数,由 442×22 \times 2 的矩阵拼成的 4×44 \times 4 大矩阵为了复合要求也必须由奇数个 11,然后这和我们上面的东西矛盾,因为奇数 ×4=\times 4= 偶数,所以,n4n\geq 4m4m \geq 4 的矩阵,一定不能满足所有条件。

那么由于 n3n\leq 3,所以我们分类讨论。

  • 对于 n=1n=1 ,答案显然为 00

  • 对于 n=2n=2 ,显然对于连续的 22 列,这两列 11 的个数必须奇偶性不相同,因为只有奇数+偶数=奇数。所以一共就只有 22 钟情况。

    1. 1111 的个数为奇数,第 2211 的个数为偶数……
    2. 1111 的个数为偶数,第 2211 的个数为奇数……

    时间复杂度 O(n)O(n)

  • 对于 n=3n=3,我们可以用状压 dpdp 来做,我们设 fi,jf_{i,j} 表示第 ii 列状态为 jj,转移的话,我们枚举前一列的合法状态,对所有可行解取个 min\min 即可,最后不要忘记加上这 11 列修改的方格数。

    不难发现,每个状态可以转移的状态数均为 22,因此此题复杂度为 O(n)O(n)1616 倍常数,但这是可以 AA 的。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
int a[15][1000010],f[1000010][11];
vector<int>v[15];
bool check(int x,int y){//判断2个状态是否能相互转移
	int a1=x&1,a2=(x>>1)&1,a3=(x>>2)&1;
	int b1=y&1,b2=(y>>1)&1,b3=(y>>2)&1;
	if((a1+a2+b1+b2)%2==0)return false;
	if((a2+a3+b2+b3)%2==0)return false;
	return true;
}
int work(int y,int x){//算修改了几个格子
	int a1=x&1,a2=(x>>1)&1,a3=(x>>2)&1;
	return (a[1][y]!=a1)+(a[2][y]!=a2)+(a[3][y]!=a3);
}
int main(){
	int n,m;read(n);read(m);
	if(n>=4&&m>=4){
		puts("-1");
		return 0;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			char ch=getchar();
			for(;ch!='0'&&ch!='1';ch=getchar());
			a[i][j]=(ch=='1');
		}
	if(n==1){
		puts("0");
		return 0;
	}
	if(n==2){
		int ans1=0,ans2=0;
		for(int j=1;j<=m;j++)
			if((a[1][j]+a[2][j])%2!=(j&1))ans1++;
		for(int j=1;j<=m;j++)
			if((a[1][j]+a[2][j])%2!=((j&1)^1))ans2++;
		cout<<min(ans1,ans2);
		return 0;
	}
	for(int i=0;i<(1<<3);i++)
		for(int j=0;j<(1<<3);j++)
			if(check(i,j))
				v[j].push_back(i);
	for(int j=0;j<(1<<3);j++)f[1][j]=work(1,j);//边界
	for(int i=2;i<=m;i++)
		for(int j=0;j<(1<<3);j++){
			f[i][j]=n*m+1;
			for(auto k:v[j]){
				f[i][j]=min(f[i][j],f[i-1][k]+work(i,j));
			}
		}
	int ans=n*m+1;
	for(int j=0;j<(1<<3);j++)ans=min(ans,f[m][j]);
	cout<<ans;
	return 0;
}
相关标签: dp 分类讨论