【模拟+递归+位运算】POJ1753-Flip Game
由于数据规模不大,利用爆搜即可。第一次用位运算写的,但是转念一想应该用递归更加快,因为位运算没有剪枝啊(qДq )
【思路】
位运算:时间效率较低(172MS),有些辜负了位运算的初衷。首先将二维数组倒序看作一个二进制数num。我们假设1代表翻转,0代表不翻转,可以发现以下规律:0 xor 1=1,1 xor 1=0;0 xor 0=0,1 xor 0=1,恰巧满足异或运算。我们假设另一个二进制数i∈[0,2^16),通过异或运算就可以模拟出所有清形。
用check和i进行&操作可以求出以哪些位置为中心进行翻转。假设当前要翻转的方格在二进制数num中所在位数为k,则翻转它时同时翻转的四个方格(假设存在的话)分别为(k-1),(k+1),(k-4),(k+4),则turn[k]=在这几位为1,其余均为0的二进制数,我在草稿纸上手工计算之后直接设在数组中。这样的好处在于,用turn与now直接进行异或操作,即可求出翻转后的情形。
之后通过求i的二进制中1的个数即可。这步操作有一个较为简便的方法,通过草稿纸上模拟就可以领悟。
{
int count = ;
while(x)
{
x = x & ( x - );
count++;
}
printf("count = %d/n", count);
}
上述方法非常实用,要牢记。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=;
int num,min;
int check[]={,,,,,,,,,,,,,,,};
int turn[]={,,,,,,,,,,,,,,,}; void init()
{
int k=;
for (int i=;i<;i++)
{
for (int j=;j<;j++)
{
char c;
scanf("%c",&c);
if (c=='w') num+=k;
k*=;
}
getchar();
}
} int mainprocess()
{
int min=INF;
for (int i=;i<;i++)
{
int now=num^i;
for (int j=;j<;j++)
if (check[j]&i)
{
now=now^turn[j];
}
if (now== || now==)
{
int ans=,x=i;
while (x)
{
x=x & (x-);
ans++;
}
if (ans<min) min=ans;
}
}
if (min==INF) return -;
else return min;
} int main()
{
init();
int output=mainprocess();
if (output==-) cout<<"Impossible"; else cout<<output;
cout<<endl;
return ;
}
此外还可以通过递归+剪枝来完成,效率较高(47ms),计算量较小。值得注意的是c++中如果将数组传入子程序,传入的其实是地址。所以必须在子程序中设置临时数组来保存当前状态,回溯时再还给原来的数组。注意,这个临时数组必须在子程序中,我第一次写的时候误将它写在了主程序中,那么临时数组就完全失去了它的意义。
剪枝:如果当前情况下需要翻转的次数已经小于最小值,则递归不需要再继续。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int INF=;
int map[][];
int dx[]={,,,-};
int dy[]={,-,,};
int ans; void recurrence(int step,int turn)
{
int tempmap[][];
if (turn>=ans) return;
if (step==)
{
int sum=;
for (int i=;i<;i++)
for (int j=;j<;j++) sum+=map[i][j];
if (sum== || sum==) ans=turn;
}
else
{
for (int k1=;k1<;k1++)
for (int k2=;k2<;k2++) tempmap[k1][k2]=map[k1][k2]; for (int i=;i<;i++)
{
if (i==)
{
int x=step/,y=step%;
map[x][y]=-map[x][y];
for (int d=;d<;d++)
if (x+dx[d]>= && x+dx[d]< && y+dy[d]>= && y+dy[d]<) map[x+dx[d]][y+dy[d]]=-map[x+dx[d]][y+dy[d]];
}
recurrence(step+,turn+i);
for (int k1=;k1<;k1++)
for (int k2=;k2<;k2++) map[k1][k2]=tempmap[k1][k2];
}
}
} int main()
{
ans=INF;
for (int i=;i<;i++)
{
char c;
for (int j=;j<;j++)
{
scanf("%c",&c);
if (c=='w') map[i][j]=; else map[i][j]=;
}
getchar();
}
recurrence(,);
if (ans!=INF) cout<<ans<<endl;else cout<<"Impossible"<<endl;
}
【模拟+递归+位运算】POJ1753-Flip Game的更多相关文章
- [CSP-S模拟测试]:位运算(数学)
题目传送门(内部题72) 输入格式 输入文件$bit.in$ 每个输入文件包含$T$组测试数据.输入文件的第一行为一个整数$T$,表示数据组数.接下来$T$行,每行表示一组测试数据每组测试数据包括三个 ...
- Winner Winner【模拟、位运算】
Winner Winner 题目链接(点击) 题目描述 The FZU Code Carnival is a programming competetion hosted by the ACM-ICP ...
- UVa 818Cutting Chains (暴力dfs+位运算+二进制法)
题意:有 n 个圆环,其中有一些已经扣在一起了,现在要打开尽量少的环,使所有的环可以组成一条链. 析:刚开始看的时候,确实是不会啊....现在有点思路,但是还是差一点,方法也不够好,最后还是参考了网上 ...
- 为什么位运算可以实现加法(1、 不考虑进位的情况下位运算符中的异或^可以表示+号)(2、 位运算符中的与运算符&和左移运算符<<可以模拟加法中的进位)(3、位运算不仅可以做加法,还可以做其它的乘法减法等:计算机本质是二进制运算)
为什么位运算可以实现加法(1. 不考虑进位的情况下位运算符中的异或^可以表示+号)(2. 位运算符中的与运算符&和左移运算符<<可以模拟加法中的进位)(3.位运算不仅可以做加法,还 ...
- 神奇的Noip模拟试题 T3 科技节 位运算
3 科技节 (scifest.pas/.c/.cpp) [问题描述] 一年一度的科技节即将到来.同学们报名各项活动的名单交到了方克顺校长那,结果校长一看皱了眉头:这帮学生热情竟然如此高涨,每个人都报那 ...
- 【NOIP模拟题】“与”(位运算)
因为是与运算,所以我们可以贪心地每次找最高位的,将他们加入到新的序列中,然后每一次在这个新的序列继续找下一个位. 然后最后序列中任意两个的与运算的值都是一样的且是最大的. #include <c ...
- 模拟赛T5 : domino ——深搜+剪枝+位运算优化
这道题涉及的知识点有点多... 所以还是比较有意思的. domino 描述 迈克生日那天收到一张 N*N 的表格(1 ≤ N ≤ 2000),每个格子里有一个非 负整数(整数范围 0~1000),迈克 ...
- poj1753,Flip Game,ArrayDeque<Node>
Flip Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 30449 Accepted: 13232 Descr ...
- js中的位运算
按位运算符是把操作数看作一系列单独的位,而不是一个数字值.所以在这之前,不得不提到什么是"位": 数值或字符在内存内都是被存储为0和 1的序列,每个0和1被称之为1个位,比如说10 ...
随机推荐
- 日常开发技巧:使用notify-send发送通知
背景 在终端执行一些需要较长时间的命令时,会切换到别的界面.但为了知道是否执行完成,需要时不时地切换过去看一眼.很麻烦. 解决方式 为了减少这种麻烦,可以使用notify-send,发送桌面通知.no ...
- Linux 入门记录:十三、Linux 扩展权限
一.默认权限 每一个终端都有一个 umask 属性,是用来确定新建文件或目录的默认权限的“掩码”(mask 有“掩码”的含义,至于 u,后面说). Linux 中一般有默认的权限掩码,使用命令 uma ...
- monkey测试===什么是monkey测试(系列一)转
本文转自:http://www.cnblogs.com/liu-ke/p/4353926.html Monkey工具使用 一. 什么是Monkey Monkey是Android中的一个命令行工具,可以 ...
- c语言简单实现telnet客户端
c语言简单实现telnet客户端 http://blog.csdn.net/haiwenchen/article/details/69944118
- C 实现有追求的线程池 后续
引言 -_- 还是老套路开局 很久以前写过一个有追求的线程池 -> C 实现有追求的线程池 探究 讲述的是一种思路, 并且实现了. 可以一用. 最近在详细搞simplec 框架. 准备发布个正式 ...
- linux sort排序命令
1 sort的工作原理 sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出. 2 sort的-u选项 在输出行中去除重复行. $ ...
- ASP .NET CORE 部署linux 系统上的所需要的sdk 使用链接
https://www.microsoft.com/net/learn/get-started/linuxopensuse
- hdu 1243(LCS变形)
反恐训练营 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submi ...
- Python中的PIL
转自:http://blog.csdn.net/yockie/article/details/8498301 介绍 把Python的基础知识学习后,尝试一下如何安装.加载.使用非标准库,选择了图像处理 ...
- .net 杂项
vs 打印信息到输出窗口 : System.Diagnostics.Debug.WriteLine("打印信息到输出窗口,但是只能在Debug版本运行,到了release版本中,Debug类 ...