穷举(四):POJ上的两道穷举例题POJ 1411和POJ 1753
下面给出两道POJ上的问题,看如何用穷举法解决。
【例9】Calling Extraterrestrial Intelligence Again(POJ 1411)
Description
A message from humans to extraterrestrial intelligence was sent through the Arecibo radio telescope in Puerto Rico on the afternoon of Saturday November 16, 1974. The message consisted of 1679 bits and was meant to be translated to a rectangular picture with 23 x 73 pixels. Since both 23 and 73 are prime numbers, 23 x 73 is the unique possible size of the translated rectangular picture each edge of which is longer than 1 pixel. Of course, there was no guarantee that the receivers would try to translate the message to a rectangular picture. Even if they would, they might put the pixels into the rectangle incorrectly. The senders of the Arecibo message were optimistic.
We are planning a similar project. Your task in the project is to find the most suitable width and height of the translated rectangular picture. The term "most suitable" is defined as follows. An integer m greater than 4 is given. A positive fraction a/b less than or equal to 1 is also given. The area of the picture should not be greater than m. Both of the width and the height of the translated picture should be prime numbers. The ratio of the width to the height should not be less than a/b nor greater than 1. You should maximize the area of the picture under these constraints.
In other words, you will receive an integer m and a fraction a/b. It holds that m > 4 and 0 < a/b <= 1. You should find the pair of prime numbers p, q such that pq <= m and a/b <= p/q <= 1, and furthermore, the product pq takes the maximum value among such pairs of two prime numbers. You should report p and q as the "most suitable" width and height of the translated picture.
Input
The input is a sequence of at most 2000 triplets of positive integers, delimited by a space character in between. Each line contains a single triplet. The sequence is followed by a triplet of zeros, 0 0 0, which indicates the end of the input and should not be treated as data to be processed.
The integers of each input triplet are the integer m, the numerator a, and the denominator b described above, in this order. You may assume 4 < m <= 100000 and 1 <= a <= b <= 1000.
Output
The output is a sequence of pairs of positive integers. The i-th output pair corresponds to the i-th input triplet. The integers of each output pair are the width p and the height q described above, in this order.
Each output line contains a single pair. A space character is put between the integers as a delimiter. No other characters should appear in the output.
Sample Input
5 1 2
99999 999 999
1680 5 16
1970 1 1
2002 4 11
0 0 0
Sample Output
2 2
313 313
23 73
43 43
37 53
(1)编程思路。
对于输入的m,通过穷举找到一组质数对(p,q),满足条件:1)p、q均为质数,且p<=q<=m;2)p*q<=m;3)a/b <= p/q;4)p*q取得最大值。
首先确定p和q的穷举范围,由于m不超过100000,因此p和q不会超过50000(因为最小的质数为2,2*50000=100000)。进一步考虑,由于1<=a<=b<=1000,所以a/b的最小值为1/1000=0.001,因此,p/q>=0.001。而当m达到最大100000时,100000/11=9090.9,质数2、3、5、7除以9091均小于0.001,因此,可将p、q的范围进一步缩小到取2~9091之间的质数。
先将小于9092的所有质数求出来,并存放在数组prime中,记下质数的个数num。
用二重循环穷举所有的p(prime[0]~prime[num-1])、q(p~prime[num-1])组合,对每种组合去判断是否符合条件,如果满足,将p*q与当前最大值max比较,保存最大的p*q。
(2)源程序。
#include <iostream>
using namespace std;
int main()
{
int flag[9092]={0},prime[5000]={0};
int num=0,m,a,b,p,q,max,i,j;
double s;
for (i=2;i<9092;i++)
{
if (flag[i]==0)
{
prime[num++]=i;
for (j=2*i;j<9092;j+=i)
flag[j]=1;
}
}
cin>>m>>a>>b;
while(m>0)
{
max=0;
s=1.0*a/b;
for(i=0; i<=num-1; i++)
{
if(prime[i]>m) break;
for(j=i;j<=num-1;j++)
{
if(prime[j]>m||prime[j]*prime[i]>m||1.0*prime[i]/prime[j]<s)
break;
if(prime[j]*prime[i]>max)
{
max=prime[i]*prime[j];
p=prime[i];
q=prime[j];
}
}
}
cout<<p<<" "<<q<<endl;
cin>>m>>a>>b;
}
return 0;
}
【例10】Flip Game(POJ 1753)
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
(1)编程思路1。
因为任何一个格子翻偶数次和翻0次的效果是一样的,翻奇数次和翻1次的效果也是一样的。因此,一个格子要么翻1次,要么不翻。另外翻格子的顺序对最终的结果是没有影响的,也就是说先翻一号格子再翻二号格子和先翻二号格子再翻一号格子的效果是一样。
这样,可以对十六个格子中的每个格子翻或不翻两种情况进行穷举,总的穷举次数为216=65536次。
用一个一维数组int bits[16]来保存16个格子的初始状态,黑色的格子置为1,白色的置为0。则题目中图示的状态可描述为bits[16]={ 1,0,1,0,0,0,0,0,1,1,0,1,1,0,0,1}。
题目图示翻转格子bits[8],需进行操作
bits[8]=!bits[8]=0,
bits[4]=!bits[4]=1, (bits[i - 4] = !(bits[i - 4]);)
bits[9]=!bits[9]=0, (bits[i + 1] = !(bits[i + 1]);)
bits[12]=!bits[12]=0, (bits[i + 4] = !(bits[i + 4]);)
由于格子8处于最左边,它的左边没有格子,因此bits[i - 1] = !(bits[i - 1])不能操作。
这样,翻转格子8后,状态变为{ 1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,1}。
用变量minCnt记录所需的最小的翻格子个数,初始值设为17,因为最多翻16个格子。
用循环 for(i = 0; i<65536; i++)对16个格子的翻或不翻的组合情况进行穷举。对每种情况i,判断i对应的二进制数中的第j位(最低位设为0位,则0<=j<=15)是否为1,如果为1,则翻转格子j。一种组合情况翻转完成后,判断当前状态是否为纯色,如果是,记录所翻转格子数(实际上是整数i对应二进制数中1的个数)的最小值。
(2)源程序1。
#include <iostream>
using namespace std;
// 判断当前格局是否为纯色
int isSolidColor(int bits[], int len)
{
int i = 0;
for (i = 0; i < len - 1; i++)
if (bits[i] != bits[i + 1])
return 0;
return 1;
}
// 改变第i个格子及其周围格子的颜色(0<=i<=15)
void changeColor(int bits[], int i)
{
bits[i] = !(bits[i]);
int x = i/4;
int y = i%4;
if (y < 3)
bits[i + 1] = !(bits[i + 1]);
if (y > 0)
bits[i - 1] = !(bits[i - 1]);
if (x > 0)
bits[i - 4] = !(bits[i - 4]);
if (x < 3)
bits[i + 4] = !(bits[i + 4]);
}
int main()
{
char c;
int bits[16],new_bits[16];
int cnt,minCnt = 17;
int i,j,k;
for(i = 0; i<16; i++) // 将输入的格局保存到数组bits中
{
cin >> c;
if(c == 'b')
bits[i] = 1;
else
bits[i] = 0;
}
for(i = 0; i<65536; i++) // 对65536种组合情况进行穷举
{
for (j = 0; j < 16; j++) // 为保证初始格局不被修改,缓存到new_bits
new_bits[j] = bits[j];
cnt = 0;
k=1;
for (j = 0;j < 16;j++) // 对当前组合情况的16个二进制位进行穷举
{
if (i & k) // 当前j位为1,则需翻转格子j,并计数
{
cnt++;
changeColor(new_bits, j);
}
k=k<<1;
}
if (isSolidColor(new_bits, 16)) // 判断是否达到纯色
if (cnt < minCnt) minCnt = cnt;
}
if (minCnt==17) cout << "Impossible" << endl;
else cout << minCnt << endl;
return 0;
}
(3)编程思路2。
在上面的思路1中,用数组bits[16]来保存一种格局。实际上,可以用一个0~65535之间的整数value来保存一种格局。该整数value对应的16个二进制数位中为1的位j代表格子j是黑色。由于一个16位的二进制数从低位到高位的权值依次为1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,因此,将一种格局映射为value值可用一个简单循环来实现:
char c;
for(i = 0; i<16; i++)
{
cin >> c;
if(c == 'b')
value += bitVal[i];
}
其中,int bitVal[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
例如,题目图示所给出的格局映射为的value值为39685,对应二进制数为1001101100000101。即39685=32768+4096+2048+512+256+4+1。
翻转一个格子是将它及其周围的格子翻转,可以看成对应二进制位变反。将一个整数的某个二进制位变反可以通过对该位用1进行异或运算实现。因此,对于当前的格局value,若要翻转某个格子i(0<=i<=15),只需将value异或一个特定的整数c[i]即可。
例如,按题目的格局value(值为39685),要翻转第8个格子,由于涉及到4个格子的变反,异或的11二进制数值应为1001100010000,及0x1310。
39685^(0x1310)的值34837,对应二进制数为1000100000010101,转换为格局为
bwbw
bwww
wwwb
wwwb
用一个数组cTable[16]来保存翻转每个格子应异或的特定整数,该数组初始化为:
int cTable[16] = {0x13,0x27,0x4E,0x8C,0x131,0x272,0x4E4,0x8C8,0x1310,0x2320,
0x4E40,0x8C80,0x3100,0x7200,0xE400,0xC800};
同样用循环for(i = 0; i<65536; i++)对16个格子的翻或不翻的组合情况进行穷举。对于穷举的每组情况,记录每一次使得value == 0||value == 65535的翻格子数,保存翻格子数目最小的数值。
(4)源程序2。
#include <iostream>
using namespace std;
int main()
{
int cTable[16] = {0x13,0x27,0x4E,0x8C,0x131,0x272,0x4E4,0x8C8,0x1310,0x2320,
0x4E40,0x8C80,0x3100,0x7200,0xE400,0xC800};
int bitVal[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
int i,j,value = 0, chgVal;
int cnt,minCnt = 17;
char c;
for(i = 0; i<16; i++)
{
cin >> c;
if(c == 'b')
value += bitVal[i];
}
for(i = 0; i<65536; i++)
{
cnt = 0;
chgVal = value;
for (j = 0;j < 16;j++)
if (i & bitVal[j])
{
cnt++;
chgVal ^= cTable[j];
}
if(chgVal==0 || chgVal==65535)
if (cnt < minCnt) minCnt = cnt;
}
if (minCnt==17) cout << "Impossible" << endl;
else cout << minCnt << endl;
return 0;
}
(5)换一种思路穷举。
前面的两种思路是按0~65535的顺序穷举,这样的顺序所选取的格子数目是不连续的。而题目要得到的是所需翻转的最少的格子数。因此,穷举的顺序最好从格子的数目这一角度出发。即当0个格子被翻转时,看对应的格局是否为纯色;否则选择1个格子进行翻转(有16种选择),看翻转后的格局是否为纯色;否则再选择2个格子进行翻转(有120种选择),看翻转后的格局是否为纯色;……;最后选择16个格子进行翻转。在这个过程中,只要有纯色的格局出现,就结束穷举过程,输出相应的被选择的格子个数。如果16个格子都被翻转了,还是没变成纯色,则输出“Impossible”。
因此,这种思路的关键问题是怎样得到从16个格子中选取n个格子的所有组合。
(6)组合情况的递归实现。
设将从1~len共len个数中选取count个不同的数,并存放于数组take中的操作抽象为函数void combineTest(int take[], int len, int count,const),则这个操作过程可以采用递归实现:
在count~len中选择一个数i(count<=i<=len)赋给take[count-1];(之所以选择的最小数为count,是因为要选出count个数,选了一个大数i的话,得留比i小的count-1个数)
再从1~i共i个数中选取count-1个不同的数,即递归调用combineTest(take, i - 1, count – 1);
递归结束条件为count==1。
编写的递归函数如下:
void combineTest(int take[], int len, int count,const int NUM)
{
int i,j;
for (i = len; i >= count; i--)
{
take[count - 1] = i;
if (count >1)
combineTest(take, i - 1, count - 1, NUM);
else
{
for (j = NUM - 1; j >=0; j--)
{
cout<<take[j]<<" ";
}
cout<<endl;
}
}
}
编写如下的程序段来测试递归函数combineTest。
int n,m;
cin>>n>>m;
int *take = new int [m];
combineTest(take,n, m, m);
delete [] take;
运行这段程序,输入5和3,可得到如下所示的结果。
5
3
5 4 3
5 4 2
5 4 1
5 3 2
5 3 1
5 2 1
4 3 2
4 3 1
4 2 1
3 2 1
Press any key to continue
(7)按新思路改写思路1的源程序3。
#include <iostream>
using namespace std;
// 判断当前格局是否为纯色
int isSolidColor(int bits[], int len)
{
int i = 0;
for (i = 0; i < len - 1; i++)
if (bits[i] != bits[i + 1])
return 0;
return 1;
}
// 改变第i个格子及其周围格子的颜色(0<=i<=15)
void changeColor(int bits[], int i)
{
bits[i] = !(bits[i]);
int x = i/4;
int y = i%4;
if (y < 3)
bits[i + 1] = !(bits[i + 1]);
if (y > 0)
bits[i - 1] = !(bits[i - 1]);
if (x > 0)
bits[i - 4] = !(bits[i - 4]);
if (x < 3)
bits[i + 4] = !(bits[i + 4]);
}
void combine(int take[], int len, int count, const int NUM, int &cnt, int bits[])
{
int i;
for (i = len; i >= count; i--)
{
if (cnt!=0) break;
take[count - 1] = i - 1;
if (count > 1)
combine(take, i-1, count-1, NUM, cnt, bits);
else
{
int j = 0;
int new_bits[16];
for (j = 0; j < 16; j++)
new_bits[j] = bits[j];
for (j = NUM - 1; j >=0; j--)
{
changeColor(new_bits, take[j]);
}
if (isSolidColor(new_bits, 16))
cnt = NUM;
}
}
}
int main()
{
char c;
int bits[16];
int i=0;
for(i = 0; i<16; i++)
{
cin >> c;
if(c == 'b')
bits[i] = 1;
else
bits[i] = 0;
}
if (isSolidColor(bits, 16))
cout<<0<<endl;
else
{
int j;
for (j = 1; j <= 16; j++)
{
int *take = new int [j];
int cnt = 0;
combine(take,16, j, j, cnt, bits);
if (cnt==j)
{
cout<<cnt<<endl;
delete [] take;
break;
}
delete [] take;
}
if (j == 17)
cout<<"Impossible"<<endl;
}
return 0;
}
(8)按新思路改写思路2的源程序4。
#include <iostream>
using namespace std;
int cTable[16] = {0x13,0x27,0x4E,0x8C,0x131,0x272,0x4E4,0x8C8,0x1310,0x2320,
0x4E40,0x8C80,0x3100,0x7200,0xE400,0xC800};
int bitVal[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
void combine(int take[], int len, int count, const int NUM, int &cnt, int value)
{
int i,j,chgVal;
for (i = len; i >= count; i--)
{
if (cnt!=0) break;
take[count - 1] = i - 1;
if (count > 1)
combine(take, i-1, count-1, NUM, cnt, value);
else
{
chgVal = value;
for (j = NUM - 1; j >=0; j--)
chgVal ^= cTable[take[j]];
if(chgVal==0 || chgVal==65535)
cnt = NUM;
}
}
}
int main()
{
int i,j,cnt,value = 0;
char c;
for(i = 0; i<16; i++)
{
cin >> c;
if(c == 'b')
value += bitVal[i];
}
if(value==0 || value==65535)
cout<<0<<endl;
else
{
for (j = 1; j <= 16; j++)
{
cnt = 0;
int *take = new int [j];
combine(take,16, j, j, cnt, value);
if (cnt==j)
{
cout<<cnt<<endl;
delete [] take;
break;
}
delete [] take;
}
if (j == 17)
cout<<"Impossible"<<endl;
}
return 0;
}
将上述四个源程序依次提交到POJ,可得到如图1所示的结果。由图可以看出,源程序4的执行效率最好。
图1 POJ显示的解决“问题1753”的四种源程序的评判情况
穷举(四):POJ上的两道穷举例题POJ 1411和POJ 1753的更多相关文章
- 两道二分coming~
第一道:poj 1905Expanding Rods 题意:两道墙(距离L)之间架一根棒子,棒子受热会变长,弯曲,长度变化满足公式( s=(1+n*C)*L),求的是弯曲的高度h. 首先来看这个图: ...
- 两道面试题,带你解析Java类加载机制
文章首发于[博客园-陈树义],点击跳转到原文<两道面试题,带你解析Java类加载机制> 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Gr ...
- 【转】两道面试题,带你解析Java类加载机制(类初始化方法 和 对象初始化方法)
本文转自 https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html 关键语句 我们只知道有一个构造方法,但实际上Ja ...
- 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester
这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...
- 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制
你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...
- POJ-1088滑雪,典型的动态规划题,与NYOJ-10skiing一样,但NYOJ上时限是3s,用搜索可以过,但在POJ上就超时了~~
滑雪 Time Limit: 1000MS Memory Limit: 65536k ...
- js 从两道面试题加深理解闭包与箭头函数中的this
壹 ❀ 引 在本文之前我已经花了两个篇幅专门介绍了JavaScript中的闭包与this,正好今早地铁上看到了两道面试题,试着做了下发现挺有意思,所以想单独写一篇文章来记录解析过程.若你对于闭包与t ...
- [正经分析] DAG上dp两种做法的区别——拓扑序与SPFA
在下最近刷了几道DAG图上dp的题目. 要提到的第一道是NOIP原题<最优贸易>.这是一个缩点后带点权的DAG上dp,它同时规定了起点和终点. 第二道是洛谷上的NOI导刊题目<最长路 ...
- 简易搭建git仓库、关联远程和本地仓库方法。克隆仓库方法。同一台电脑上创建两个git ssh key方法。
一,在github上建仓库 react-js-antd-demo: 二:将远程仓库与本地仓库关联 git remote add origin git@github.com:begin256/react ...
随机推荐
- javascript之存储数据-cookie,localStorage,sessionStorage
cookie: 存储一些简单的数据,以文本形式放到本地,大小4kb 存储:document.cookie='name=value' 取值:document.cookie(字符串) 期限:expires ...
- 使用mysqlfrm恢复frm表结构,获得建表语句
1.mysqlfrm 是一个恢复性质的工具,用来读取.frm文件并从该文件中找到表定义数据生成CREATE语句. 下载链接:https://downloads.mysql.com/archives/u ...
- (水题)洛谷 - P1149 - 火柴棒等式
https://www.luogu.org/problemnew/show/P1149 一开始还分类重复了.在非0的dfs中居然赋值了0,脑残得一笔. 其实就按 $lead0$ 分类就好了, $lea ...
- DFS系列 POJ(自认为的讲解)
C - Sum It Up POJ1564 题意: 给你一个N,然后给你一堆数The numbers in each list appear in nonincreasing order, and t ...
- bzoj 4259 4259: 残缺的字符串【FFT】
和bzoj 4503 https://www.cnblogs.com/lokiii/p/10032311.html 差不多,就是再乘上一个原串字符 有点卡常,先在点值下算最后一起IDFT #inclu ...
- IT兄弟连 Java Web教程 Servlet的生命周期
JavaWeb应用的生命周期由Servlet容器来控制,而Servlet作为JavaWeb应用的最核心的组件,其生命周期也由Servlet容器来控制.Servlet的生命周期可以分为3个阶段:初始化阶 ...
- Luogu P1754球迷购票问题【dp/卡特兰数】By cellur925
题目传送门 虽然是水dp,但我感到还是有些无从下手== f[i][j]表示还剩i个50元没考虑,j个100元没考虑的方案数,可有转移f[i][j]=f[i-1][j]+f[i][j-1] 但其实它也可 ...
- jmeter远程运行GUI多用户负载
1.在Jmeter控制机的bin目录下找到jmeter.properties文件并修改”remote_hosts”,增加负载机IP,多个IP使用英文逗号隔开,修改后要重启Jmeter.如下图: 2.添 ...
- iOS bounds vs frame
斯坦福iOS开发课程的白胡子大叔的PPT解释得淋漓尽致!
- 跟我一起玩Win32开发(16):ListView的多个视图
在上一个例子中,我们只用到了ListView的Report视图,也就是详细视图.本文我们再把上一篇文章中所用的例子进行一下扩展,例子源码可以到俺的资源区下载. 我们为ListView中显示的数据加上图 ...