入门专题第二题 poj 2965 The Pilots Brothers' refrigerator

题目链接http://poj.org/problem?id=2965

此题与1753类似,同样用dfs枚举,学长学姐们应该就是想让我们先练一下这些基础吧。。

思路几乎来说都一样,不赘述了。

但是TLE了大半天,太弱了orz。。。

问题在于我用了跟1753一样的方法,将这种情况下的所有点找到才一起翻转,,这样就会出现一个问题,那么就是比如选五个点 0 1 2 3 4 5 找到后一起翻,之后选到0 1 2 3 4 6,也是找到后一起翻,,然后这样0 1  2 3 4 这几个点都重复翻,,想了一下这样导致做了很多的无用功,真该TLE。。。之前想了好久不知道怎么改,最后想到在找到点的时候就直接翻转,不要这个点的时候再翻回来。。这样就不会有那么多无用功。。比如0 1 2 3 4 每个找出来后都直接翻了,那么最后一个放5 或者6等其他的都不会再翻前面的,与之前相比大大缩短了时间。。

#include<stdio.h>
#include<string.h>
char s[20];
int a[20];
int visit[20],ans[20];
int flag=0;
int is_win()
{
	int i;
	for(i=0;i<16;i++)
		if(a[i]==0) return 0;
	return 1;
}
void flip(int depth)
{
	int i,j,k,n;
	int x=ans[depth]/4;
	int y=ans[depth]%4;
	for(j=0;j<16;j++)
		if(j/4==x||j%4==y) a[j]=!a[j];
}
void dfs(int depth,int start,int r)
{
	int i,n;
	if(flag==1) 
	return ;
	if(depth==r)
	{
		flag=is_win();
		if(flag)
		{
			printf("%d\n",depth);	
			for(i=0;i<depth;i++)
			printf("%d %d\n",ans[i]/4+1,ans[i]%4+1);
		}	
		return ;
	}
	else
	for(i=start;i<=16-r+depth;i++)
	{
		if(!visit[i])
		{
			visit[i]=1;
			ans[depth]=i; //保存需要翻的点 
			flip(depth); //选取这个位置翻转 
			dfs(depth+1,i+1,r);
			flip(depth); //将这个位置翻回来 
			visit[i]=0;
		}
	}
}
int main ()
{
	int i,j,r;
	flag=0;
	memset(visit,0,sizeof(visit));
	for(i=0;i<16;i++)
	{
		char ch;
		ch=getchar();
		if(ch=='\n') i--;
		else s[i]=ch;
		if(s[i]=='+') a[i]=0;
		else a[i]=1;
	}
	if(is_win()) printf("0\n");
	else 
	for(i=1;i<=16;i++)
	{
		memset(ans,0,sizeof(ans));
		dfs(0,0,i);
		if(flag==1) break;
	}
	return 0;
}

别人写的二维数组的dfs,值得我学习,确实这方面还很弱。附上链接http://blog.csdn.net/lyy289065406/article/details/6642597

下面是链接中的代码

/*代码一:DFS+Enum*/

//Memory Time 
//240K  641MS 

//本题由于要输出每次翻转的棋子,因此不适宜用BFS,应该使用DFS输出完整路径

#include<iostream>
using namespace std;

bool lock[10][10]={false};
bool flag;
int step;
int ri[16],cj[16];

bool isopen(void)
{
	for(int i=3;i<7;i++)
		for(int j=3;j<7;j++)
			if(lock[i][j]!=true)
				return false;
	return true;
}

void flip(int row,int col)               //其实参考POJ1753的翻棋方法也是可以做出来的,但是会超时不通过
{                                        //超时的原因就是翻棋时有太多多余的操作
	lock[row][col]=!lock[row][col];      //POJ1753使用6x6矩形,多余操作只有周围的“一圈”翻棋!
	for(int i=3;i<=6;i++)                //这里使用10x10矩形,多余操作有“三圈”翻棋!
		lock[i][col]=!lock[i][col];      //其实用位运算就可以只使用4x4矩形,大大降低时间复杂度,根本没有多余操作,但是程序会很复杂,不好看
	for(int j=3;j<=6;j++)
		lock[row][j]=!lock[row][j];
	return;
}

void dfs(int row,int col,int deep)
{
	if(deep==step)
	{
		flag=isopen();
	    return;
	}

	if(flag||row==7)return;

	flip(row,col);
	ri[deep]=row;
	cj[deep]=col;

	if(col<6)
		dfs(row,col+1,deep+1);
	else
		dfs(row+1,3,deep+1);

	flip(row,col);
	if(col<6)
		dfs(row,col+1,deep);
	else
	    dfs(row+1,3,deep);
	return;
}

int main(void)
{
	char temp;
	int i,j;
	for(i=3;i<7;i++)
		for(j=3;j<7;j++)
		{
			cin>>temp;
			if(temp=='-')
				lock[i][j]=true;
		}

	for(step=0;step<=16;step++)
	{
		dfs(3,3,0);
		if(flag)break;
	}

	cout<<step<<endl;
	for(i=0;i<step;i++)
		cout<<ri[i]-2<<' '<<cj[i]-2<<endl;
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值