牛可乐的翻转游戏(super detailed)

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

哈哈,我也成标题党了

参考文章:https://www.cnblogs.com/BlankYang/p/16280128.html大佬已经尽力了,只是我太菜一开始还是没看懂,但也为我提供了一些思路,接下来我继续补充一些个人自己写时遇到的问题与难点。

题目我就不描述了,直接讲解一下吧:

这道题的考点是状压dp,也就是状态压缩+动态规划,每一行的棋子用一个数代表,其二进制的每一位都表示对应棋子的状态,1为黑棋,0为白棋。

至于动态规划:因为题目的思路是通过改变第i行来使第i-1行全为0(题目要求是全0或全1,但我在输入的时候分两块棋盘了,第1块和输入一样,把他变为0相当与最后全为白棋;第二块和第一块的0,1颠倒了,这样把他变为0相当与最后全为黑棋,这样只需要一个算法就可以了)。就这样从第1行开始把第0行全变为0,然后改变第2行把第1行全变为0......直到最后一行,如果它全为0,就成功了,否则失败。(注意:虽然是从第1行开始翻棋,但是第0行也要翻,还要翻pow(2,m)次,其目的在于更新第一行的状态,不在于把上一行全变为0,其实一旦第一行确定了后面也都只能执行一种操作了,本题的关键就是遍历第行所有可能更新后的状态,找到最好的那一种)

以下是代码,结构还是比较清晰的,若有不得当的地方欢迎大家批评指正。

#include<bits/stdc++.h>
using namespace std;

int n,m;
int change[105]={0};   //记录每一行的操作(异或值)
int pointer[105]={0};  //记录每一行变成0前的一个状态
int num_1(int a){      //求一个数对应的二进制数中1的个数
	int out=0;
	while (a)
	{
		out+=a&1;
		a=a>>1;
	}
	return out;
}

int solve(vector<int>a){
	int minout=(1<<31)-1;
	for(int i=0;i<(1<<m);i++){
		change[0]=i;    //这里很重要,对第0行进行更新,这样可能取得更好的效果,不要直接从第一行开始;一共可能的更新有(1<<m)-1种,所以for循环是这个目的
		int sum=0;
		pointer[0]=a[0];
		for(int j=0;j<n;j++){
			sum+=num_1(change[j]);
			pointer[j]=pointer[j] ^change[j] ^(change[j]>>1) ^( (change[j]<<1) & ((1<<m)-1) );
			pointer[j+1]=a[j+1]^change[j]; 
			change[j+1]=pointer[j];
		}
		if(pointer[n-1]==0){
			minout=min(minout,sum);
		}
	}
	return minout;
}

int main(){
	cin>>n>>m;
	vector<int>black(n+5);
	vector<int>white(n+5);
	char temp;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>temp;
			if(temp=='1'){
				black[i]=(black[i]<<1)+1;
				white[i]=white[i]<<1;
			}
			else{
				black[i]=black[i]<<1;
				white[i]=(white[i]<<1)+1;
			}
		}
	}//分两种棋盘
	
	int out=min(solve(black),solve(white));//全黑全白里面挑一个小的
	if(out>n*m){
		cout<<"Impossible"<<'\n';
	}
	else{
		cout<<out<<'\n';
	}
}

一开始确实挺难,但理解就好了,第一次接触状压dp可能需要一两天,但会了之后就真的十分通透,感觉能多干两碗米饭。最后,祝愿学算法的宝宝们都能坚持下去哦!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值