【上海交大oj】畅畅的牙签袋(改)(枚举+模拟)

题目描述

上次妄图遮掩显示器的企图失败了,所以畅畅很不开心。这次,他换了一种牙签袋卷土重来了。这次他选择了十字形的牙签袋(即每贴一个牙签袋都会影响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 }
View Code

 

posted @ 2015-07-17 22:43  文_码  阅读(336)  评论(0编辑  收藏  举报