题目链接
题目链接 https://vjudge.net/problem/POJ-2965
原题目
The game “The Pilots Brothers: following the stripy elephant” has a quest where a player needs to open a refrigerator.
There are 16 handles on the refrigerator door. Every handle can be in one of two states: open or closed. The refrigerator is open only when all handles are open. The handles are represented as a matrix 4х4. You can change the state of a handle in any location [i, j] (1 ≤ i, j ≤ 4). However, this also changes states of all handles in row i and all handles in column j.
The task is to determine the minimum number of handle switching necessary to open the refrigerator.
Input
The input contains four lines. Each of the four lines contains four characters describing the initial state of appropriate handles. A symbol “+” means that the handle is in closed state, whereas the symbol “?” means “open”. At least one of the handles is initially closed.
Output
The first line of the input contains N – the minimum number of switching. The rest N lines describe switching sequence. Each of the lines contains a row number and a column number of the matrix separated by one or more spaces. If there are several solutions, you may give any one of them.
Sample Input
-+-- ---- ---- -+--
Sample Output
6 1 1 1 3 1 4 4 1 4 3 4 4
题目大意
有一个4x4的矩阵代表十六个开关 有的开关可以打开 有的开关是关闭的 每一个开关都可以改变状态 每一次改变状态都会导致同一行以及同一列的状态一起被改变 问你如何通过最短开关次数让最终状态为所有开关全是打开状态 输出次数以及每次操作的坐标
思路
一 BFS+状态压缩
通过一个十六位的二进制数字枚举当前状态 不断将更改了旧状态中十六个点的某一个点的新的状态送到队列当中 当新的状态代表着所有开关全都打开时退出队列 其中注意每个状态不能重复 即要写一个vis数组判断状态有没有经历过。
值得一提的是这里我向锋队学习了一个特殊的手写队列他可以利用结构体中的f找到前一个结点。
时间复杂度为O(2^16)
详情见代码
BFS方法代码
#include<iostream>
using namespace std;
const int maxn=1<<17;
int vis[1<<17];
int head,tail;
int mp[5][5];
struct node{int f,s,c;//
}q[maxn];
int change(int num,int s)
{//cout<<s<<endl;for(int i=(num/4)*4;i<(num/4)*4+4;i++) {s^=1<<i;}for(int i=num%4;i<16;i+=4){s^=1<<i;}s^=1<<num;//cout<<s<<endl;return s;
}
int getans(int now)
{if(q[now].f==-1)return 0;return getans(q[now].f)+1;
}
void pans(int now)
{if(q[now].f==-1)return ;pans(q[now].f);cout<<q[now].c/4+1<<" "<<q[now].c%4+1<<endl;
}
int bfs()
{while(head<tail){node now=q[head];if(now.s==0){//cout<<head<<endl;return head;}for(int i=0;i<16;i++){node noww;noww.s=change(i,now.s);noww.f=head;//记录上一个节点noww.c=i;if(vis[noww.s]==1)continue;vis[noww.s]=1;q[tail++]=noww;}head++;}return -1;
}
int main()
{node tmp;tmp.f=-1;tmp.c=-1;tmp.s=0;for(int i=1;i<=4;i++){string s;cin>>s;for(int j=0;j<4;j++){if(s[j]=='+'){mp[i][j+1]=1;tmp.s+=1<<((i-1)*4+j);//cout<<tmp.s<<endl;}}}q[tail++]=tmp;vis[tmp.s]=1;int ans=bfs();cout<<getans(ans)<<endl;pans(ans);return 0;
}
二思维
对于每一个开关若是进行奇数次开关 则状态改变 若是进行偶数次开关则状态不变
对于某一个关闭的开关 i,j 我们在他的同一行同一列(共七块地方)开关一次 则除了该点(i,j)
其他的地方不会有改变
我们对每一个关闭的开关进行一次这样的操作,最后如果改变次数是奇数的点则需要进行开关。
思维方法代码
#include<iostream>
using namespace std;
int mp[5][5];
int time[5][5];
void flag(int x,int y)
{for(int i=1;i<=4;i++){time[i][y]++;time[x][i]++;}time[x][y]--;
}
int main()
{for(int i=1;i<=4;i++){string s;cin>>s;for(int j=0;j<4;j++){if(s[j]=='+'){mp[i][j+1]=1;}}}for(int i=1;i<=4;i++){for(int j=1;j<=4;j++){if(mp[i][j]==1){flag(i,j);}}}int ans=0;for(int i=1;i<=4;i++){for(int j=1;j<=4;j++){if(time[i][j]%2==1){ans++;}}} cout<<ans<<endl;for(int i=1;i<=4;i++){for(int j=1;j<=4;j++){if(time[i][j]%2==1){cout<<i<<" "<<j<<endl;}}} return 0;
}