UVa_11464_Even Parity_机智地搜索

既然自己那么水,看书的时候遇到不会的就先跳过吧。

今天再次见到队友直接变成了一条狗。


题意:

给你一个n*n的01矩阵,要求把尽量少的0变成1,使得每个元素的上下左右元素(如果存在的话)之和均为偶数。


Input:

第一行为数据组数t。每组数据第一行为正整数n(1<=n<=15);接下来n行包含n个非0即1的整数,相邻整数之间用一个空格隔开。

Output:

对于每组数据,输出被改变的元素的最小个数,无解则输出-1。



  朴素的想法就是枚举每一个点是否从0变成1,但15*15个元素深搜枚举要2^15,太大,考虑回溯法。

  从左上往右下遍历的话,显然右下的元素会受到左上元素的影响,那么如果第一行已经确定了,易证下面的元素都是确定的,因此只需要枚举第一行的所有情况,然后就可以在多项式时间内算出整个矩阵或者是求出这种情况不可能。

  用dfs实现,先遍历第一行的情况,再算出矩阵,边算边判断。


  代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define mxn 20
int a[mxn][mxn];
int n;
bool flag[mxn][mxn];
void change(int i,int j){
	if(i)	flag[i-1][j]=1-flag[i-1][j];
	if(j)	flag[i][j-1]=1-flag[i][j-1];
	if(i!=n-1)	flag[i+1][j]=1-flag[i+1][j];
	if(j!=n-1)	flag[i][j+1]=1-flag[i][j+1];
}
int dfs(int x,int ans){
	if(x==n){
		//cout<<"hehe "<<endl;
		//for(int i=0;i<n;++i)
		//	cout<<a[0][i]<<" ";
		//cout<<endl;
		memset(flag,0,sizeof(flag));
		for(int i=0;i<n;++i)
			for(int j=0;j<n;++j){
				int tem=0;
				if(j!=n-1)	tem+=a[i][j+1];
				if(i!=n-1)	tem+=a[i+1][j];
				if(i)	tem+=a[i-1][j];
				if(j)	tem+=a[i][j-1];
				flag[i][j]=tem%2;
			}
		for(int i=0;i<n-1;++i)
			for(int j=0;j<n;++j){
				if(!flag[i][j])	continue;
				if(!a[i+1][j]){	change(i+1,j);++ans;	}
				else return -1;
			}
		for(int i=0;i<n;++i)
			if(flag[n-1][i])	return -1;
		//cout<<"ans "<<ans<<endl;
		return ans;
	}
	int ret=dfs(x+1,ans);
	if(a[0][x]==1)	return ret;
	a[0][x]=1-a[0][x];
	//cout<<"x: "<<x<<endl;
	int tem=dfs(x+1,ans+1);
	if(ret==-1)	ret=tem;
	else	ret=(tem==-1)?ret:min(tem,ret);
	a[0][x]=1-a[0][x];
	return ret;
}
int main(){
	int cs;
	scanf("%d",&cs);
	for(int CS=1;CS<=cs;++CS){
		scanf("%d",&n);
		for(int i=0;i<n;++i)
			for(int j=0;j<n;++j)
				scanf("%d",&a[i][j]);
		printf("Case %d: ",CS);
		printf("%d\n",dfs(0,0));
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值