恢复实力第一波
Part1 搜索
POJ 1753
Time Limit: 1000MS | Memory Limit: 65536K | |
bmissions: 47349 | Accepted: 20236 |
Description
- Choose any one of the 16 pieces.
- Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any).
Consider the following position as an example:
bwbw
wwww
bbwb
bwwb
Here "b" denotes pieces lying their black side up and "w" denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become:
bwbw
bwww
wwwb
wwwb
The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal.
Input
Output
Sample Input
bwwb bbwb bwwb bwww
Sample Output
4一般来说,最短路的问题的搜索一般是广搜。当然网上也有大神的DFS大法
把每个棋盘当作一个状态,枚举反转每个棋子的状态,将状态压入队列BFS下去
关键的问题在于如何判断状态的重复,不然会RE掉
注意到两个问题:
1.一个棋子反转两次等价于没有反转,所以可以根据棋子来判重
2.一共有16个格子,每个只有两种状态。好像和二进制有点相似,可以把每一个棋盘化成一个数来判重(表示真的不好想,没有这种意识)
这样一来就大体解决了
先用二进制处理初始棋盘,再从此状态枚举BFS下去。注意一下处理就好。
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
struct Flip
{
int a[10][10],wi;
}t,p;
bool vis[80000];
queue<Flip> q;
bool xs()
{
long sum,k=1;
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
{
sum+=k*p.a[i][j];
k*=2;
}
if(vis[sum])
return true;
else
vis[sum]=true;
return false;
}
bool work(int i,int j)
{
int sum=0;
p=t;
p.a[i][j]=!p.a[i][j];
p.a[i-1][j]=!p.a[i-1][j];
p.a[i+1][j]=!p.a[i+1][j];
p.a[i][j-1]=!p.a[i][j-1];
p.a[i][j+1]=!p.a[i][j+1];
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
sum+=p.a[i][j];
if(sum==16||sum==0)
return true;
p.wi++;
if(!xs())
q.push(p);
return false;
}
int main()
{
t.wi=1;
char c;
int sum=0;
memset(vis,false,sizeof(vis));
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
{
cin>>c;
if(c=='b')
t.a[i][j]=1;
else
t.a[i][j]=0;
sum+=t.a[i][j];
}
if(sum==16||sum==0)
{
printf("0\n");
return 0;
}
p=t;
if(!xs())
q.push(t);
while(!q.empty())
{
t=q.front();
q.pop();
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
if(work(i,j))
{
printf("%d\n",t.wi);
return 0;
}
}
printf("Impossible\n");
return 0;
}
Status | Accepted |
---|---|
Time | 391ms |
Memory | 1604kB |
Length | 1187 |
对于处理的方式还有一个优化的版本,因为是用二进制的方式存储的状态,所以考虑到位运算异或。
用打表的方式计算出反转某个棋子需要异或的数,然后处理的时候直接异或就方便了很多。
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct Node
{
int state;
int step;
};
bool vis[65536];
int change[16]=
{
51200,58368,29184,12544,
35968,20032,10016,4880,
2248,1252,626,305,
140,78,39,19
};
int bfs(int state)
{
int i;
memset(vis,false,sizeof(vis));
queue<Node> q;
Node cur,next;
cur.state=state;
cur.step=0;
q.push(cur);
vis[state]=true;
while(!q.empty())
{
cur=q.front();
q.pop();
if(cur.state==0||cur.state==0xffff)
return cur.step;
for(int i=0;i<16;i++)
{
next.state=cur.state^change[i];
next.step=cur.step+1;
if(vis[next.state])
continue;
if(next.state==0||next.state==0xffff)
return next.step;
vis[next.state]=true;
q.push(next);
}
}
return -1;
}
int main()
{
int i,j,state,ans;
char ch[5][5];
while(scanf("%s",ch[0])!=EOF)
{
for(int i=1;i<4;i++)
scanf("%s",ch[i]);
state=0;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
state<<=1;
if(ch[i][j]=='b')
state+=1;
}
}
ans=bfs(state);
if(ans==-1)
puts("Impossible");
else
printf("%d\n",ans);
}
return 0;
}
Status | Accepted |
---|---|
Time | 16ms |
Memory | 640kB |
Length | 1173 |
Time Limit: 1000MS | Memory Limit: 65536K | |||
Total Submissions: 27328 | Accepted: 10557 | Special Judge |
Description
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
和上一个题很相似的题目,多了回溯的路径输出与变化方式。
第一反应就是相似的做法,然后维护一个链表。
结果RE
代码如下,
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<stack>
using namespace std;
int size=0;
bool vis[66666];
struct Node
{
int state,step,fa,pos,size;
}way[66666];
int change[16]={
0xf888,0xf444,0xf222,0xf111,
0x8f88,0x4f44,0x2f22,0x1f11,
0x88f8,0x44f4,0x22f2,0x11f1,
0x888f,0x444f,0x222f,0x111f
};
int bfs(int state)
{
int n;
queue<Node > q;
Node cur,next;
cur.state=state;
cur.step=0;
cur.size=0;
cur.fa=0;
q.push(cur);
vis[state]=true;
while(!q.empty())
{
cur=q.front();
q.pop();
if(cur.state==0)
return cur.size;
for(int i=0;i<16;i++)
{
next.state=cur.state^change[i];
next.step=cur.step+1;
next.pos=i;
next.fa=cur.size;
if(vis[next.state])
continue;
next.size=++size;
way[next.size]=next;
if(next.state==0)
return next.size;
vis[next.state]=true;
q.push(next);
}
}
}
int main()
{
int n,state=0;
char ch[4][4];
for(int i=0;i<4;i++)
scanf("%s",&ch[i]);
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
{
state<<=1;
if(ch[i][j]=='+')
state+=1;
}
memset(vis,false,sizeof(vis));
int ans=bfs(state);
printf("%d\n",way[ans].step);
stack<int > st;
while(way[ans].fa)
{
st.push(ans);
ans=way[ans].fa;
}
printf("%d %d\n",way[ans].pos/4+1,way[ans].pos%4+1);
while(!st.empty())
{
ans=st.top();
st.pop();
printf("%d %d\n",way[ans].pos/4+1,way[ans].pos%4+1);
}
return 0;
}
结果悲催的调了好久,至今也不知道哪里错了
去网上查了查题解,发现大神的做法就是厉害
对于一个开关若要让他的状态变化则需要让它对应的行和列上的都操作一次,这样除了这个开关其他的状态不变
对于每一个开着的开关都做这样的变化,最后再处理最短路径。
和上一个题一样,对同一个开关操作两次效果一样,所以在上一次操作时没改变一次就加一,最后%2就是最短路径
最后扫一遍输出就好了。
#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
char c;
int a[6][6],b[6][6];
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
{
cin>>c;
if(c=='+')
a[i][j]=1;
else
a[i][j]=0;
b[i][j]=a[i][j];
}
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
if(a[i][j]==1)
{
for(int k=1;k<=4;k++)
{
b[i][k]++;
b[k][j]++;
}
}
int sum=0;
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
if(b[i][j]%2)
sum++;
printf("%d\n",sum);
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
if(b[i][j]%2)
printf("%d %d\n",i,j);
return 0;
}