poj 2965

The Pilots Brothers' refrigerator

Time Limit: 1000MS

Memory Limit: 65536K

Total Submissions: 11826

Accepted: 4373

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 rowi 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

证明是在讨论里看到的 有了证明很简单 贴下代码 自己思考

这也说明了去acm并不仅仅是编程他是用程序思维数学方法最精炼的解决问题 最后用代码表示出来

证明:要使一个为'+'的符号变为'-',必须其相应的行和列的操作数为奇数;

可以证明,如果'+'位置对应的行和列上每一个位置都进行一次操作,则整个图只有这一'+'位置的符号改变,其余都不会改变.

> 设置一个4*4的整型数组,初值为零,用于记录每个点的操作数,那么在每个'+'上的行和列的的位置都加1,得到结果模2

(因为一个点进行偶数次操作的效果和没进行操作一样,这就是楼上说的取反的原理),

然后计算整型数组中一的

> 个数即为操作数,一的位置为要操作的位置(其他原来操作数为偶数的因为操作并不发生效果,因此不进行操作)

#include <iostream>
#include <queue>
using namespace std;
int map[5][5];
intans;
char a[8];
void count(inti,int j)
{
int k;
for(k=0;k<4;k++)
{
map[i][k]++;
map[k][j]++;
}
map[i][j]--;
}
void result()
{
ans=0;
inti,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
ans+=map[i][j]%2;
}
void output()
{
cout<<ans<<endl;
inti,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
if(map[i][j]%2==1)
cout<<i+1<<' '<<j+1<<endl;
}

int main()
{
memset(map,0,sizeof(map));
int i;
int j;
for(i=0;i<4;i++)
{
cin>>a;
for(j=0;j<4;j++)
if(a[j]=='+')
count(i,j);
}
result();
output();
return 0;
}

此提一共有三种解法:

1.枚举

最朴素的算法,但是一开始我居然不知道如何来枚举。大概的原理是:以位置1,1开始变化。得到16种位置的最小解法,然后选最少的一个就OK

2.BFS

一开始,我想到的就是这个解法。原来还认为是枚举,但是仔细看看应该是BFS。因为是记录给自己看的,所以解法不说。

3.直接给结果

这题和之前的黑白子差不多。不过那题我是BFS过的。所以这题,想看看枚举人家怎么做的。但是没想到搜索到了这种解法,对比了一下discuss和他的讲解。下面将代码贴出来。

1// http://www.cppblog.com/Yusi-Xiao/archive/2010/07/05/77385.html

2// 先看一个简单的问题,如何把'+'变成'-'而不改变其他位置上的状态?

3// 答案是将该位置(i,j)及位置所在的行(i)和列(j)上所有的handle更新一次。

4// 结果该位置被更新了7,相应行(i)和列(j)handle被更新了4,剩下的被更新了2.

5// 被更新偶数次的handle不会造成最终状态的改变.

6// 因此得出高效解法,在每次输入碰到'+'的时候, 计算所在行和列的需要改变的次数

7// 当输入结束后,遍历数组,所有为奇数的位置则是操作的位置,而奇数位置的个数之和则是最终的操作次数.

8// PS:该题不会有"Impossible"的情况.

9

10#include <stdio.h>

11

12#define Len 4

13

14void main()

15{

16 inthandles[Len][Len] = {0};

17 int i, j, k, step = 0;

18 char c;

19

20 // 核心算法,统计翻转的总次数

21 for (i = 0; i < Len; ++i)

22 {

23 for (j = 0; j < Len; ++j)

24 {

25 scanf("%c\n", &c);

26 if ('+' == c)

27 {

28 handles[i][j]++;

29 for (k = 0; k < Len; ++k)

30 {

31 handles[i][k]++;// 这种算法重复计算i,j处,但是对于只需要判断奇偶来说无所谓

32 handles[k][j]++;

33 }

34 }

35 }

36 }

37 // 统计奇数的个数

38 for (i = 0; i < Len; ++i)

39 {

40 for (j = 0; j < Len; ++j)

41 {

42 if (handles[i][j] % 2)

43   {

44 step++;

45 }

46 }

47 }

48 printf("%d\n", step);

49

50 // 打印奇数的位置

51 for (i = 0; i < Len; ++i)

52 {

53 for (j = 0; j < Len; ++j)

54 {

55 if (handles[i][j] % 2)

56   {

57 printf("%d %d\n", i + 1, j + 1);

58 }

59 }

60 }

61}

转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1299077378

提示:这题和POJ1753翻转棋的思想是一致的,需要注意的是要求输出翻转过程,因此不能用BFS,必须用DFS(找到目标后,还要过程回溯)

与POJ1753相比,这题还要注意翻棋的方法,若不注意会大大浪费时间导致超时,因为是整行整列翻转,在边界处会出现很多多余操作。代码中详细说明

同样本题有两种方法,Enum和Bit ,下面分别贴出这两种代码

//Memory Time

//240K641MS

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

#include<iostream>

usingnamespacestd;

boollock[10][10]={false};

boolflag;

intstep;

intri[16],cj[16];

boolisopen(void)

{

for(inti=3;i<7;i++)

for(intj=3;j<7;j++)

if(lock[i][j]!=true)

returnfalse;

returntrue;

}

voidflip(introw,intcol) //其实参考POJ1753的翻棋方法也是可以做出来的,但是会超时不通过

{//超时的原因就是翻棋时有太多多余的操作

lock[row][col]=!lock[row][col];//POJ1753使用6x6矩形,多余操作只有周围的一圈翻棋!

for(inti=3;i<=6;i++)//这里使用10x10矩形,多余操作有三圈翻棋!

lock[i][col]=!lock[i][col];//其实用位运算就可以只使用4x4矩形,大大降低时间复杂度,根本没有多余操作,但是程序会很复杂,不好看

for(intj=3;j<=6;j++)

lock[row][j]=!lock[row][j];

return;

}

voiddfs(introw,intcol,intdeep)

{

if(deep==step)

{

flag=isopen();

return;

}

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

flip(row,col);

ri[deep]=row;

cj[deep]=col;

 

dfs(row,col+1,deep+1);

flip(row,col);

 

dfs(row,col+1,deep);

 

return;

}

intmain(void)

{

chartemp;

inti,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;

return0;

}

消除行号

=============华丽的分割线=============

1
2
3//Memory Time
4//240K563MS
5
6//本题由于要输出每次翻转的棋子,因此不适宜用BFS,应该使用DFS输出完整路径
7
8#include<iostream>
9usingnamespacestd;
10
11int chess; //棋盘状态
12int step;
13bool flag=false;
14intri[16],cj[16];
15
16boolisopen(void)
17{
18if(chess==0xFFFF)
19returntrue;
20else
21returnfalse;
22}
23
24void flip(int bit)
25{
26chess=chess^(0x1<<bit);//对翻转位取反
27int row=bit/4;
28int col=bit%4;
29for(intc=0;c<4;c++)
30chess=chess^(0x1<<(row*4+c));//对全行取反
31for(intr=0;r<4;r++)
32chess=chess^(0x1<<(r*4+col));//对全列取反
33return;
34}
35
36voiddfs(intbit,intdeep)
37{
38if(deep==step)
39{
40
flag=isopen();
41return
;
42}
43

44if(flag||bit>15)return;
45
46int row=ri[deep]=bit/4;
47int col=cj[deep]=bit%4;
48
49flip(bit);
50

51dfs(bit+1,deep+1);
52
53
54
55flip(bit);
56

57dfs(bit+1,deep);
58
59
60return;
61}
62
63int main(void)
64{
65
66
67char temp;
68inti,j;
69for(i=0;i<4;i++)
70for(j=0;j<4;j++)
71{
72
cin>>temp;
73if(temp=='-')
74chess=chess^(0x1<<(i*4+j));
75}
76

77
78
79for(step=0;step<=16;step++)
80{
81dfs(0,0
);
82if(flag)
83break;
84}
85
86cout<<step<<endl;
87for(i=0;i<step;i++)
88cout<<ri[i]+1<<' '<<cj[i]+1<<endl;
89return0;
90}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值