下面给出两道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的更多相关文章

  1. 两道二分coming~

    第一道:poj 1905Expanding Rods 题意:两道墙(距离L)之间架一根棒子,棒子受热会变长,弯曲,长度变化满足公式( s=(1+n*C)*L),求的是弯曲的高度h. 首先来看这个图: ...

  2. 两道面试题,带你解析Java类加载机制

    文章首发于[博客园-陈树义],点击跳转到原文<两道面试题,带你解析Java类加载机制> 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Gr ...

  3. 【转】两道面试题,带你解析Java类加载机制(类初始化方法 和 对象初始化方法)

    本文转自 https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html 关键语句 我们只知道有一个构造方法,但实际上Ja ...

  4. 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester

    这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...

  5. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  6. POJ-1088滑雪,典型的动态规划题,与NYOJ-10skiing一样,但NYOJ上时限是3s,用搜索可以过,但在POJ上就超时了~~

    滑雪 Time Limit: 1000MS                    Memory Limit: 65536k                                        ...

  7. js 从两道面试题加深理解闭包与箭头函数中的this

     壹 ❀ 引 在本文之前我已经花了两个篇幅专门介绍了JavaScript中的闭包与this,正好今早地铁上看到了两道面试题,试着做了下发现挺有意思,所以想单独写一篇文章来记录解析过程.若你对于闭包与t ...

  8. [正经分析] DAG上dp两种做法的区别——拓扑序与SPFA

    在下最近刷了几道DAG图上dp的题目. 要提到的第一道是NOIP原题<最优贸易>.这是一个缩点后带点权的DAG上dp,它同时规定了起点和终点. 第二道是洛谷上的NOI导刊题目<最长路 ...

  9. 简易搭建git仓库、关联远程和本地仓库方法。克隆仓库方法。同一台电脑上创建两个git ssh key方法。

    一,在github上建仓库 react-js-antd-demo: 二:将远程仓库与本地仓库关联 git remote add origin git@github.com:begin256/react ...

随机推荐

  1. bzoj2144

    二分+lca 我们把向中间缩看成向上爬,向两边走看成向下爬,那么就相当于找出两个状态的lca,如果相邻的差是(a,b),a<b,那么向中间走就是(a,b-a)或(b-a,a),这个东西很像更相减 ...

  2. Bootstrap Notify

    https://github.com/mouse0270/bootstrap-notify $.notify('Hello World', { offset: { x: 50, y: 100 } }) ...

  3. C++ 指针p1 p2,p1-p2 与*p1-*p2的区别

    p1-p2 指 指针的地址值相减,计算两个指针之间的偏移量 *p1-*p2 指 指针指向的内存地址里面存的数值相减

  4. pl/sql developer安装使用即时客户端

    pl/sql developer安装使用即时客户端 背景:由于Oracle的安装比较麻烦,大部分人都使用远程Oracle,而Oracle Instantclient Basic package可以解决 ...

  5. 任务12:Bind读取配置到C#实例

    将json文件的配置转换成C#的实体 新建项目: Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definition ...

  6. eclipse导入工程报Invalid project description(转载)

    转自:http://blog.sina.com.cn/s/blog_a2eab3000101k3r7.html 昨天新搭建的环境,今天把以前的项目导入eclipse时报错: 说的是我导入的项目与wor ...

  7. lightoj1200 【完全背包】

    题意: 有一个能放W重的袋子,然后妻子给了老公一列清单,每个item会有价格,数量,重量. 首先得满足老婆的要求,然后在可装的地方输出最大还能拿多少使得拿的东西的钱最多. 注意标题是thief,我想大 ...

  8. 给Clouderamanager集群里安装基于Hive的大数据实时分析查询引擎工具Impala步骤(图文详解)

    这个很简单,在集群机器里,选择就是了,本来自带就有Impala的. 扩展博客 给Ambari集群里安装基于Hive的大数据实时分析查询引擎工具Impala步骤(图文详解)

  9. Spring事务引发dubbo服务注册问题

    文章清单 1. 问题 2. 查找bug过程 3. 解决方案 使用spring boot+dubbo写项目,一个服务,之前是正常的,后来调用方出现空指针异常,第一反应提供方出了问题. 1. 看控制台,服 ...

  10. spring boot eureka client

    eureka client @EnableDiscoveryClient @SpringBootApplication public class DemoApplication { public st ...