回归1—pre_train_国庆不回家1

恢复实力第一波

Part1 搜索

POJ 1753

Flip Game
Time Limit: 1000MS Memory Limit: 65536K
bmissions: 47349 Accepted: 20236

Description

Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it's black or white side up. Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules: 
  1. Choose any one of the 16 pieces. 
  2. 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

The input consists of 4 lines with 4 characters "w" or "b" each that denote game field position.

Output

Write to the output file a single integer number - the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it's impossible to achieve the goal, then write the word "Impossible" (without quotes).

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
速度明显快了不少。。。
The Pilots Brothers' refrigerator
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;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值