• Fliptile (POJ


    给你一个01矩阵,矩阵大小为M x N。(1 <= M , N <= 15)
    每次操作选择一个格子,使得该格子与上下左右四个格子的值翻转。
    至少多少次操作可以使得矩阵中所有的值变为0?
    请输出翻转方案,若没有方案,输出"IMPOSSIBLE” 。

    Input
    第一行输入两个数:M和N。(1 <= M , N <= 15)
    接下来M行,每行N个数,其值只为0或1。

    Output
    输出M行,每行N个数。
    每个数代表该位置翻转次数

    Sample Input
    4 4
    1 0 0 1
    0 1 1 0
    0 1 1 0
    1 0 0 1

    Sample Output
    0 0 0 0
    1 0 0 1
    1 0 0 1
    0 0 0 0

    刚看题毫无头绪,既要反转次数最少又要字典序最小,暴力枚举复杂度得2^(m*n),太大了,状压也不知道该怎么做。
    仔细观察可以发现,要想使矩阵全部为0,那么每个1都要反转,而且每个位置最多反转一次,如果反转两次就又回去了做的是无用功。既然每个1都要反转,那么对于[i,j]这个位置,如果[i-1,j]为1的话,[i,j]就必须要反转,所以上一行的状态确定了,这一行必须反转的位置就也确定了,所以第1行的状态确定了,第2行就确定了,那第3行业确定了…,那么总的反转次数就确定了。
    所以首先要确定第一行的状态(第一行不一定全为0,因为第二行的时候会把第一行全变为0),这样到第最后一行的时候前面全部变为0了,但最后一行不一定,判断一下即可。
    还有一个条件是反转的字典序最小,因为第一行状态确定,后面所有行的饭庄位置就确定了,所以第一行的反转状态字典序最小整个的字典序就最小了,所以从小到大枚举第一行的反转状态,取反转次数最小的那个。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=100;
    int n,m,a[N][N],cnt[N][N],num[N][N]; //cnt保存当前状态的反转矩阵,num保存最终的答案矩阵
    void reverse(int x,int y)//以x,y为中心反转
    {
    	a[x][y]=1-a[x][y];
    	if(x>1) a[x-1][y]=1-a[x-1][y];
    	if(y>1) a[x][y-1]=1-a[x][y-1];
    	if(y<m) a[x][y+1]=1-a[x][y+1];
    	if(x<n) a[x+1][y]=1-a[x+1][y];
    }
    int dfs(int x,int y)
    {
    	int t=0;//统计反转次数
    	if((x==n+1))
    	{
    		bool flag=1;
    		for(int j=1;j<=m;j++) if(a[n][j]) flag=0;
    		if(cnt[x][y]) reverse(x,y);//将矩阵恢复原样
    		if(flag) return t;
    		else return 0x3f3f3f3f;//如果不可行返回0x3f3f3f3f这个状态的次数就一定不会比答案tmp小,就不会出错了
    	}
    	if(a[x-1][y])
    	{
    		reverse(x,y);
    		t++;
    		cnt[x][y]=1;
    	}
    	if(y<m) t+=dfs(x,y+1);
    	else t+=dfs(x+1,1);
    	if(cnt[x][y]) reverse(x,y);//恢复矩阵
    	return t;
    }
    int tmp=0x3f3f3f3f;
    void solve(int i,int t)//正向遍历第一行,第一次判断的反转状态是0000,然后递归回m,这是的状态是0001,再递归回去就是0010,然后0011...就实现了从小到大枚举第一行的状态
    {
    	if(i==m+1)
    	{
    		for(int j=2;j<=n;j++)//将第二行一下的cnt清空
    			for(int k=1;k<=m;k++) cnt[j][k]=0;
    		t+=dfs(2,1);//计算第2行以后的反转次数
    		if(t<tmp)
    		{
    			for(int j=1;j<=n;j++)//保存反转矩阵
    				for(int k=1;k<=m;k++)
    					num[j][k]=cnt[j][k];
    			tmp=t;
    		}
    	  	return ;
    	}
    	solve(i+1,t);
    	reverse(1,i);
    	cnt[1][i]=1;
    	solve(i+1,t+1);
    	reverse(1,i);//记得恢复现场
    	cnt[1][i]=0;
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			cin>>a[i][j];
    	solve(1,0);
    	if(tmp<0x3f3f3f3f)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			for(int j=1;j<=m;j++)
    				cout<<num[i][j]<<' ';
    			puts("");
    		}
    	}
    	else puts("IMPOSSIBLE");
    }
    
  • 相关阅读:
    flask1 + jinja2 day88
    linux9
    linux8 redis集群槽+docker
    dsadfa
    redis
    aaa
    a
    题目
    java对含有中文的字符串进行Unicode编码
    Java转Double类型经纬度为度分秒格式
  • 原文地址:https://www.cnblogs.com/neflibata/p/12871748.html
Copyright © 2020-2023  润新知