1391. 畅畅的牙签袋(改)
题目描述
上次妄图遮掩显示器的企图失败了,所以畅畅很不开心。这次,他换了一种牙签袋卷土重来了。这次他选择了十字形的牙签袋(即每贴一个牙签袋都会影响5块区域,贴的时候中心点必须贴在显示器上,如果中心点在边上,则会有一些块暴露在显示器外,不过没关系,畅畅会把这些块剪掉的),所以他觉得这次一定能把整个显示器贴的满满的。
贴了又撕,撕了又贴,怔了一会儿后,他发现整个显示器被整的凹凸不平,非常难受。因为强迫症,他决定要把每一块区域都贴到有偶数层牙签袋(就是说这次可以重叠的铺了),而他现在想知道他最少需要多少牙签袋才能做到。
还是因为那个原因,这个活就是你们的了。
输入格式
输入第一行有两个整数,H和W(1<=H,W<=16),代表显示器大小。
接下来有H行,每行W个整数L(0<=L<=10000),代表每个区域已有的牙签袋层数。
输出格式
输出最少所需的牙签袋数目,若无论如何都无法完成要求,则输出-1.
Sample Input1
1 2
0 1
Sample Output1
-1
Sample Input2
2 2
0 1
2 3
Sample Output2
2
考试的时候思维定势了,以为又是动归,但是看起来好麻烦,于是就没有勇气尝试,实在不该。实际上这题就是要用暴力求解,可以发现,当上一行的状态确定的时候,下一行有唯一解,也就是上一行为单数的这一行必须要铺才可以,那么我们只需枚举第一行的所有状态,看一看这个状态下模拟下来能不能达到要求,找到一个最小的就好了,枚举第一行可以写一个w重的循环,也可以递归(dfs),或者直接用压缩的状态(二进制)枚举,模拟就是一行一行根据上一行的状态改变这一行和下一行的状态。
代码:
1 #include <iostream> 2 using namespace std; 3 4 int begin[16][16]; 5 int nowline[16]; 6 int nextline[16]; 7 int lastline[16]; 8 int main(){ 9 int h,w,min = 9999999; 10 11 cin>>h>>w; 12 for (int i = 0;i < h;++i) 13 for (int j = 0;j < w;++j) cin>>begin[i][j]; 14 15 if (h==1){ 16 int i,cnt = 0; 17 for (i = 0;i < w;++i) if (begin[0][i] & 1){ 18 if (i+1 < w) begin[0][i+1]++; 19 if (i+2 < w) begin[0][i+2]++; 20 cnt++; 21 } 22 if (begin[0][w-1] & 1) cout<<-1<<endl; 23 else cout<<cnt<<endl; 24 return 0; 25 } 26 for (int i = 0;i < 1<<w;++i){ //枚举第一行 27 28 for (int j = 0;j < w;++j) { //初始化 29 nowline[j] = begin[0][j]; 30 nextline[j] = begin[1][j]; 31 }//for (int j = 0;j < w;++j) cout<<nowline[j]<<' ';cout<<endl; 32 // for (int j = 0;j < w;++j) cout<<nextline[j]<<' ';cout<<endl; 33 int t = 1,cnt = 0; 34 for (int j = 0;j < w;++j,t<<=1){ 35 if (i & t){ 36 nextline[j]++; 37 nowline[j]++; 38 if (j > 0) nowline[j-1]++; 39 if (j<w-1) nowline[j+1]++; 40 cnt++; 41 } 42 } 43 44 for (int j = 1;j < h-1;j++){ //开始模拟 45 for (int k = 0;k < w;++k){ 46 lastline[k] = nowline[k]; 47 nowline[k] = nextline[k]; 48 nextline[k] = begin[j+1][k]; 49 } 50 for (int k = 0;k < w;++k){ 51 if (lastline[k] & 1){ 52 nextline[k]++; 53 nowline[k]++; 54 if (k>0) nowline[k-1]++; 55 if (k<w-1) nowline[k+1]++; 56 cnt++; 57 } 58 } 59 } 60 //处理倒数第二行 61 for (int j = 0;j < w;++j) if (nowline[j] & 1) { 62 nextline[j]++; 63 if (j>0) nextline[j-1]++; 64 if (j< w-1) nextline[j+1]++; 65 cnt++; 66 } 67 //判断是否可行 68 int j = 0; 69 for (j = 0;j < w;++j){ 70 if (nextline[j] & 1) { 71 break; 72 } 73 } 74 if (j==w && cnt<min) {min = cnt;} 75 } 76 77 if (min < 9999999) cout<<min<<endl; 78 else cout<<-1<<endl; 79 80 return 0; 81 }