POJ1222、POJ3279、POJ1753 - DFS

POJ1222-EXTENDED LIGHTS OUT

POJ3279-Fliptile

POJ1753フリップゲーム

なぜ3つの質問には、それを話すように置くのでしょうか?あなたはそれができる一度~~ 3ACポイントを理解しているのであれば

各トピックの意味で話すことがまず

1.EXTENDEDは消灯します

01は、あなたに5行6列の行列を与える0はランプを代表するポイントオフになって、ランプを表す点が開いているすべてのライトがオフになるように、それは、もちろん、各ランプのスタックが押されている必要があり場合プレスランプを、図に示すように、周辺のランプは、元の状態とは逆になるであろう。


 2.Fliptile

牛の群れは、白タイルのように、01のN×m個の行列、0白、1つの黒を入力し、各ブリックを反転する方法を求め、最終的にすべての色が反転辞書に対応する行列の要求に対応し、白色であることができます最低注文。各変更の意志を反転します。不可能ではない出力


 3.Flipゲーム

道のために黒と白の円があなたを掲載し、図は出力すべての色は、フリップの数が最小の1になるために、すべての白またはすべて黒になります。

どのように失敗し、出力インポッシブルを反転している場合


3つのトピックによって、彼らが共通していることを意味しています。最後には、同じ色を最終的に必要とされかつ反転点は、状態の周りのいくつかの元の色に影響を与えるにバインドされ

そこに最初の二次及び三次不可能な状況に加えて。

以下は(考え方を導入し始めたすべてのポイントを解決することは0になるという仮定|白フリップスキーム)を、n個×m個のグリッドのために、各グリッドは、私が見つけるために、すべての状態の上に検索されている、2状態に対応します適し反転方式はできないのですか?もちろん、しかし、すべての可能性ではTアウトとなります。4つの4 * 2 ^ 16の状態、ましてや大きな上の光マトリクスので。だから、暴力的な検索は望ましいものではありません。

 

 

 

 

 

 

 

 

各操作については、これはその時点から、私たちは始めるか、上記のような状況に対応するであろうか?

因为题目要求矩阵中所有的点都变为同一色,而每个操作互相影响,所以搜索要一行一行来,但是如果后一行的全为0,当前行的某一个操作又影响到上一行和下一行,那后面的又白费了,这样要搞到什么时候??

试想一下,如果第一行的翻转方案确定了,那么第一行的翻转势必影响到第二行的翻转,第二行的翻转也会影响到第三行的翻转,一直到最后一行。。。也就是说,第一行的翻转方案会影响到整个翻转的结果。

而第一行的翻转状态有多少呢??只有2^m(列号)(用二进制对应每个点的状态)个,这比搜索所有的状态要少多了

我们还要看一个问题,因为每个点的状态只有两个,0和1,自己翻转肯定改变自己的状态,也会影响周围临近4个点的状态,周围的翻转也会影响自己。但是,有个次数的问题。就如同灯开了2次,4次,6次...到最后还是恢复成原来的状态了。

即如果当前自己为1,自己加周围4个总共翻转了5次,那么自己变成了几???5次后,自己变成了0。也就是说,这个点在周围和自己的影响下,变成了目标(0)的状态,那么,这个点还用去翻转吗??当然不用了

如果是0,一样的道理,所以总结出一点:若cnt表示当前点的状态信息(0|1),sum表示自己加周围临近的4个的所有翻转次数,那么最终这一点是否要翻转取决于(cnt +sum)% 2的值.

当前是1,临近点加自己总共翻转5次,那么自己变成0。即最终自己不需要额外翻转一次
当前是1,临近点加自己总共翻转4次,那么自己变成1,最后自己要多翻转一次才能变成0
当前是0,临近点加自己总共翻转5次,自己变成1,那么自己要多翻转一次才能变成0
当前是0,临近点加自己总共翻转4次,那么自己变成0,不需要额外翻转了

得出结论,当前点是否需要翻转取决于 (原来自己的状态+所有对自己起作用的翻转数)  % 2, 结果为1表明要翻转,为0表明不需要了。这里不清楚的可以自己多写几个看看

好了,第一行的状态数知道了,即每个点的怎么翻转知道了,该怎么求最终结果呢??

因为已经知道了第一行的所有翻转方案(0 ~ 2^m-1个),那么对所有方案搜索,直到最后一行的所有的翻转方案都是0就表明第一行的翻转方案是可取的了。。。这什么意思呢

如果搜索到最后,发现最后一行存在几个数是需要翻转的,即这几个数是在上层的翻转作用下变成了1,最后还需要把这几个1翻转一下才会变成0,但是,当你翻转这几个1,势必又影响到上一层,结果上一层又出现几个1。再往上翻,就打乱了所有的状态。所以第一行的翻转方案一定是不可行的。

要点:在第一行的翻转方案搜索中,通过搜索第一行的翻转方案,用第二行的翻转去改变第一行的状态。接下来的每一行继续用下一行去调整但前行的状态,直至全为。。。,最后观察最后一行是否需要调整,需要就没戏了,不需要说明OK了。

举个例子,第一行的翻转方案为8:111

上述过程强烈建议手动模拟一下,其实在计算机计算时,先考虑了000的方案,然后会往后找次数更少的方案,看能否找到次数最少的方案(具体看题目要求)。

贴一下代码:

  1 #include<iostream>
  2 #include<stdio.h>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<vector>
  6 #include<stack>
  7 #include<map>
  8 #include<set>
  9 #include<list>
 10 #include<queue>
 11 #include<string>
 12 #include<algorithm>
 13 #include<iomanip>
 14 using namespace std;
 15 const int maxn = 20;
 16 int H[5]={0,-1,0,1,0};
 17 int V[5]={0,0,1,0,-1};
 18 int cur[maxn][maxn];//当前结点的颜色
 19 int res[maxn][maxn];//最终结果
 20 int flip[maxn][maxn];//是否翻转存放结果
 21 int n;//
 22 int m;//
 23 
 24 bool IsIn(int x,int y)//判断是否越界
 25 {
 26     if(x>=0 && x<n && y >=0 && y <m)
 27         return true;
 28     return false;
 29 }
 30 
 31 //查询颜色 0 或 1  | 黑 或 白
 32 int GetStatus(int x,int y)
 33 {
 34     int cnt = cur[x][y];//获取当前的状态
 35     for(int i = 0 ;i < 5;i++)
 36     {
 37         int x2 = x +H[i];
 38         int y2 = y +V[i];
 39         if(IsIn(x2,y2))
 40         {
 41             cnt += flip[x2][y2];//记录周围的反转次数总和
 42         }//即周围4个加行自己5个格子的有cnt个反转过
 43     }
 44     return cnt % 2;//最终确定自己是否需要翻转
 45 }
 46 
 47 //对第一行的翻转进行搜索
 48 int SolveRow1()
 49 {
 50     for(int i = 1 ;i < n;i++)//第二行的翻转使第一行为0,第三行的翻转使得第二行为0,依次.
 51     {
 52         for(int j = 0;j < m;j++)
 53         {
 54             if(GetStatus(i-1,j))//当前为1,说明要翻转一次  为0 则不需要
 55             {
 56                 flip[i][j] = 1;//记录翻转一次
 57             }
 58         }
 59     }
 60     for(int j = 0;j< m;j++)//判断最后一行
 61     {
 62         if(GetStatus(n-1,j))//不是全0 ,即最后一行存在1 要翻转 此时没有翻转的余地了
 63         {
 64             return -1;//返回-1
 65         }
 66     }
 67     int times = 0;//如果最后一行也为0,即所有的都为0了,表明第一行的翻转是可行的
 68     for(int i = 0;i <n;i++)
 69     {
 70         for(int j = 0;j < m;j++)
 71         {
 72             times += flip[i][j];//记录整个矩阵的翻转总次数
 73         }
 74     }
 75     return times;//返回次数
 76 }
 77 
 78 void Solve()
 79 {
 80     int ans = -1;
 81     for(int i = 0;i < (1<<m);i++)// m列 共  2 ^ m 个选择方案 [0,2^m)
 82     {
 83         memset(flip,0,sizeof(flip));//初始化操作
 84         for(int j = 0;j < m;j++)
 85         {
 86             flip[0][m-j-1] = i >> j & 1 ;//1 表示要转换 0表示不动
 87         }
 88         int num = SolveRow1();//-1 无解
 89         if(num >= 0 && (ans <  0 || ans > num))
 90         {
 91             ans = num;//找出最小翻转次数
 92             memcpy(res,flip,sizeof(flip));
 93         }
 94     }
 95     if(ans < 0)
 96     {
 97         cout<<"IMPOSSIBLE"<<endl;
 98     }
 99     else//打印翻转结果
100     {
101         for(int i = 0;i < n;i++)
102         {
103             for(int j = 0;j < m;j++)
104             {
105                 cout<<res[i][j];
106                 if(j!= m -1)
107                 {
108                     cout<<" ";
109                 } 
110             }
111             cout<<endl;
112         }
113     }   
114 }
115 
116 int main()
117 {
118     while(cin>>n>>m&& n!=0 && m!=0)
119     {
120         for(int i = 0;i < n;i++)
121         {
122             for(int j = 0 ;j < m;j++)
123             {
124                 cin>>cur[i][j];
125             }
126         }
127         Solve();
128     }
129     return 0;
130 }
View Code

这里在对这段代码进行简单说明下

 1  for(int i = 0;i < (1<<m);i++)// m列 共  2 ^ m 个选择方案 [0,2^m)
 2     {
 3         memset(flip,0,sizeof(flip));//初始化操作
 4         for(int j = 0;j < m;j++)
 5         {
 6             flip[0][m-j-1] = i >> j & 1 ;//1 表示要转换 0表示不动
 7         }
 8         .. .
 9         ...
10 }

其中i表示从0到2^m次方的所有的翻转方案,就把m看成3把。flip[i][j]表明第i行第j列是否要翻转,所以第一行有2^3==8中翻转方案



 在对解题做个简要说明,POJ1222规模确定,确保可行,直接做即可。POJ3279也十分类似

对于1753,分别需要计算全部翻为白色的最少翻转次数和全部翻为黑色的最少翻转次数。其实只要将输入的b和w对应的1 和 0 调换下,两次始终求解将1 全部换成0的方案数,最终取最小值即可。

 

おすすめ

転載: www.cnblogs.com/ygsworld/p/11266329.html