链接:登录—专业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可能需要一两天,但会了之后就真的十分通透,感觉能多干两碗米饭。最后,祝愿学算法的宝宝们都能坚持下去哦!