标签:
一开始想贪心,类似启发式搜索的感觉...后来觉得不行,而且很难写。
不如就枚举。可以通过0到2^W的中的每一个数的二进制形式来对应,第一行每个位置是否作为中心点放入十字格子的情况。
当此处为0时表示不放,1时表示放。
为什么只枚举第一行的所有情况就可以了呢。
因为第一行的情况确定之后,我们可以通过推理先改变第二行某些状态,然后再根据必须把第一行充满,可以确定第二排所有必须放十字块的位置。
生成该状态数之后,调用put函数,然后先影响下一行再通过结果来确定下一行。(这个算法的根基就是,处理每一行的时候要把上一行全部填满)
所以当到了最后一行被处理过之后,判断最后一行的合法性就可以了。
因为要处理很多位置...不想搞二进制很多事情了,就用bool数组来存状态了。
#include <iostream> #include <algorithm> using namespace std; const int INF = 9999999; bool map[20][20]={0}; bool cur[20][20]={0}; int H,W; void print(){ cout<<"--------"<<endl; for (int i = 0; i < H; ++i){ for (int j=0; j < W; ++j){ cout<<cur[i][j]; } cout<<endl; } cout<<"--------"<<endl; } void copyMap(){ for (int i = 0; i < H; ++i) for (int j=0; j < W; ++j) cur[i][j] = map[i][j]; } void init(){ cin>>H>>W; for (int i = 0; i < H; ++i){ for (int j = 0; j < W; ++j){ int l; cin>>l; map[i][j] = l%2; //偶数为0 奇数为1 } } } //计算k的二进制中有多少个1 int getOnes(int k){ int ans = 0; while(k>0){ if((k&1)) //如果最右侧是1 ans++; k = k>>1;//每次扔出最右的一位; } return ans; } void put(int lineId, int state){ //枚举时我们认为一个 状态数state 每个位置的 0表示不放 1表示放 那么进行填补 for (int i = 0; i < W ; ++i) //从第一排第0个位置 到第W-1个 { if(state&(1<<i)){//表示是放的 则对两边及下方的进行填写 if(lineId>=1)//可以不用写 cur[lineId-1][i] = !cur[lineId-1][i]; cur[lineId][i] = !cur[lineId][i]; if(i>=1) cur[lineId][i-1] = !cur[lineId][i-1]; cur[lineId][i+1] = !cur[lineId][i+1]; cur[lineId+1][i] = !cur[lineId+1][i]; } } } int build(){ bool have = false; int res = INF; //首先要对第一排的放十字的所有可能情况进行枚举 //然后根据第一行的摆放情况 可以确定第二行的摆放,依次决定第三行..... for (int i = 0; i < (1<<W); ++i)//一共有2^W种放的情况 { //每次枚举前先进行copy 不能直接在map上进行修改 copyMap(); int cnt = getOnes(i);//对放的十字的个数进行记录 put(0,i); //print(); //第一排做完了 我们来根据第一排的情况 确定接下来每一排必须放十字的位置 for (int k = 1; k < H; ++k) { int curState = 0;//生成状态数 用来进行调用put函数 for (int j = 0; j < W; ++j) { if(cur[k-1][j])//如果上一行的这个地方是1 即是奇数 则肯定要在以这一行的这个位置为中心进行填补 curState += (1<<j); } cnt += getOnes(curState); put(k,curState); } //放完之后要检查最后一行是不是已经被填满 如果是的话 则说明全体都被填成偶数 bool ok = true; for (int j = 0; j < W; ++j) { if(cur[H-1][j]){ ok = false; break; } } if(ok){ have = true; res = min(res,cnt); } } if(!have) res = -1; return res; } int main(int argc, char const *argv[]) { init(); cout<<build()<<endl; return 0; }
【算法学习笔记】74. 枚举 状态压缩 填充方案 SJTU OJ 1391 畅畅的牙签袋(改)
标签:
原文地址:http://www.cnblogs.com/yuchenlin/p/sjtu_oj_1391.html