算法竞赛入门经典 第七章 总结

目录:

      • 7.1 简单枚举
      • 7.2 枚举排列
      • 7.3 子集生成

7.1 简单枚举

例题7-1 除法(Division, UVa 725)
输入正整数n,按从小到大的顺序输出所有形如abcde/fghij = n的表达式,其中a~j恰好 为数字0~9的一个排列(可以有前导0),2≤n≤79。
样例输入
62
样例输出:
79546 / 01283 = 62
94736 / 01528 = 62

#include<iostream>
using namespace std;int pjj(int m , int n){int k=0;int a[10]={0};while(m){a[k++]=m%10;m=m/10;}while(n){a[k++]=n%10;n=n/10;}   for(int i=0;i<10;++i)for(int j=i+1;j<10;++j)if(a[i]==a[j]) return -1;return 1;
}int main(){int n,k,i;cin>>n;for(i=1234;i*n<=98765;++i){k=i*n;if(pjj(i,k)==1) {cout << k << "/" ; if(i < 10000) cout <<"0"; cout << i << "=" << n <<endl;}}
}

分析:只需要枚举fghij就可以算出abcde,然后判断是否 所有数字都不相同即可

例题7-2 最大乘积(Maximum Product, UVa 11059)
输入n个元素组成的序列S,你需要找出一个乘积最大的连续子序列。如果这个最大的乘 积不是正数,应输出0(表示无解)。1≤n≤18,-10≤Si≤10。
样例输入
3
2 4-3
5
2 5 -1 2 -1
样例输出:
8
20

#include<iostream>
using namespace std; 
int main(){int n;int a[100];while(cin>>n){long long maxm=-100,tmp;int i,j;for(i=0;i<n;++i){cin>>a[i];}   for(i=0;i<n;++i){tmp=1;for(j=i;j<n;++j){tmp*=a[j];if(maxm<tmp){maxm=tmp;}}}if(maxm<=0) cout<<'0'<<endl;else cout<<maxm<<endl;}}

分析:连续子序列有两个要素:起点和终点,因此只需枚举起点和终点即可,其中起点为i,终点为j。

例题7-3 分数拆分(Fractions Again?!, UVa 10976)
输入正整数k,找到所有的正整数x≥y,使得 1/k=1/x+1/y
样例输入
2
12
样例输出
2
1/2 = 1/6 + 1/3
1/2 = 1/4 + 1/4
8
1/12 = 1/156 + 1/13
1/12 = 1/84 + 1/14
1/12 = 1/60 + 1/15
1/12 = 1/48 + 1/16
1/12 = 1/36 + 1/18
1/12 = 1/30 + 1/20
1/12 = 1/28 + 1/21
1/12 = 1/24 + 1/24

#include<iostream>
using namespace std;
int main(){double k,x;int i,sum;while(cin>>k){sum=0;for(i=k+1;i<=2*k;++i){x=i*k/(i-k);if(x==(int)x)sum++;}cout<<sum<<endl;for(i=k+1;i<=2*k;++i){x=i*k/(i-k);if(x==(int)x)cout<<"1/"<<k<<'='<<"1/"<<x<<'+'<<"1/"<<i<<endl;}}} 

分析
既然要求找出所有的x、y,枚举对象自然就是x、y了。可问题在于,枚举的范围如何? 从1/12=1/156+1/13可以看出,x可以比y大很多。难道要无休止地枚举下去?当然不是。由 于x≥y,有 1/k<=1/y+1/y因此 ,即y≤2k。这样,只需要在2k范围之内枚举y,然后根据y尝试 计算出x即可

7.2 枚举排列

例7-2-1:输入整数n,按字典序从小到大的顺序输出前n个数的 所有排列。
分析:两个序列的字典序大小关系等价于从头开始第一个不相同位置处的大 小关系。例如,(1,3,2) < (2,1,3),字典序最小的排列是(1, 2, 3, 4,…, n),最大的排列是(n, n-1, n-2,…, 1)。n=3时,所有排列的排序结果是(1, 2, 3)、(1, 3, 2)、(2, 1, 3)、(2, 3, 1)、(3, 1, 2)、 (3, 2, 1)。
以1开头的排列的特点是:第一位是1,后面是2~9的排列。根据字典序的定义,这些2 ~9的排列也必须按照字典序排列。换句话说,需要“按照字典序输出2~9的排列”,不过需 注意的是,在输出时,每个排列的最前面要加上“1”。这样一来,所设计的递归函数需要以 下参数:
1.已经确定的“前缀”序列,以便输出。
2.需要进行全排列的元素集合,以便依次选做第一个元素

void print_permutation(序列A, 集合S) 
{    if(S为空) 输出序列A;    else 按照从小到大的顺序依次考虑S的每个元素v    {        print_permutation(在A的末尾填加v后得到的新序列, S-{v});    } 
}

下面考虑程序实现。不难想到用数组表示序列A,而集合S根本不用保存,因为它可以 由序列A完全确定——A中没有出现的元素都可以选。C语言中的函数在接受数组参数时无法 得知数组的元素个数,所以需要传一个已经填好的位置个数,或者当前需要确定的元素位置 cur,代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
int a[1000];
void print_permutation(int n,int *a,int cur);
int main(){int n;cin>>n;print_permutation( n, a, 0);
}void print_permutation(int n,int *a,int cur){if(cur==n) {for(int i=0;i<n;++i) printf("%d ",a[i]);printf("\n")    ;}else{for(int i=1;i<=n;++i){//改变前缀int ok = 1;for(int j=0;j<cur;++j){//如果i已经在A[0]~A[cur-1]出现过,则不能再选if(a[j]==i) ok = 0;}if(ok){a[cur]=i;print_permutation( n, a, cur + 1);//每一个前缀的可能排序}}}
} 

递归边界是S为空 的情形,这很好理解:现在序列A就是一个完整的排列,直接输出即可。接下来按照从小到大的顺序考虑S中的每个元素,每次递归调用以A开头.
循环变量i是当前考察的A[cur]。为了检查元素i是否已经用过,上面的程序用到了一个 标志变量ok,初始值为1(真),如果发现有某个A[j]==i时,则改为0(假)。如果最终ok仍 为1,则说明i没有在序列中出现过,把它添加到序列末尾(A[cur]=i)后递归调用。

例7-2-2生成可重集的排列

如果把问题改成:输入数组P,并按字典序输出数组A各元素的所有全排列,则需要对 上述程序进行修改——把P加到print_permutation的参数列表中,然后把代码中的if(A[j] == i) 和A[cur] = i分别改成if(A[j] == P[i])和A[cur] = P[i]。这样,只要把P的所有元素按从小到大的顺序排序,然后调用print_permutation(n, P, A, 0)即可,如下面代码所示。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[1000];int p[1000];
void print_permutation(int *p,int n,int *a,int cur);
int main(){int n,i;cin>>n;for(i=0;i<n;++i){cin>>p[i];}sort(p,p+n);print_permutation(p, n, a, 0);
}void print_permutation(int *p,int n,int *a,int cur){int i;if(cur==n) {for(int i=0;i<n;++i) printf("%d ",a[i]);printf("\n")    ;}else{for(int i=0;i<n;++i){//改变前缀int ok = 1;for(int j=0;j<cur;++j){//如果i已经在A[0]~A[cur-1]出现过,则不能再选if(a[j]==p[i]) ok = 0;}if(ok){a[cur]=p[i];print_permutation( p, n, a, cur + 1);//每一个前缀的可能排序}}}
} 

这个方法看上去不错,可惜有一个小问题:输入1 1 1后,程序什么也不输出(正确答案 应该是唯一的全排列1 1 1),原因在于,这样禁止A数组中出现重复,而在P中本来就有重 复元素时,这个“禁令”是错误的。
一个解决方法是统计A[0]~A[cur-1]中P[i]的出现次数c1,以及P数组中P[i]的出现次数 c2。只要c1 < c2,就能递归调用

结果又如何呢?输入1 1 1,输出了27个1 1 1。遗漏没有了,但是出现了重复:先试着把 第1个1作为开头,递归调用结束后再尝试用第2个1作为开头,递归调用结束后再尝试用第3 个1作为开头,再一次递归调用。可实际上这3个1是相同的,应只递归1次,而不是3次。

换句话说,我们枚举的下标i应不重复、不遗漏地取遍所有P[i]值。由于P数组已经排过 序,所以只需检查P的第一个元素和所有“与前一个元素不相同”的元素,即只需在“for(i = 0; i < n; i++)”和其后的花括号之前加上“if(!i || P[i] != P[i-1])”即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[1000];int p[1000];
void print_permutation(int *p,int n,int *a,int cur);
int main(){int n,i;cin>>n;for(i=0;i<n;++i){cin>>p[i];}sort(p,p+n);print_permutation(p, n, a, 0);
}void print_permutation(int *p,int n,int *a,int cur){int i;if(cur==n) {for(int i=0;i<n;++i) printf("%d ",a[i]);printf("\n")    ;}else for(int i = 0; i < n; i++) { if(!i || p[i] != p[i-1]){int c1 = 0,c2 = 0;  for(int j = 0; j < cur; j++) if(a[j] == p[i]) c1++;  for(int j = 0; j < n; j++) if(p[i] == p[j]) c2++;  if(c1 < c2) {    a[cur] = p[i];    print_permutation(p,n, a, cur+1);  } }}} 

总结:
如果某问题的解可以由多个步骤得到,而每个步骤都有若干种选择(这些候 选方案集可能会依赖于先前作出的选择),且可以用递归枚举法实现,则它的工作方式可以 用解答树来描述。
这里写图片描述

例7-2-3,利用next_permutation解答
枚举所有排列的另一个方法是从字典序最小排列开始,不停调用“求下一个排列”的过 程。如何求下一个排列呢?C++的STL中提供了一个库函数next_permutation

#include<cstdio> 
#include<algorithm> //包含next_permutation 
using namespace std; 
int main( ) {  int n, p[10];  scanf("%d", &n);  for(int i = 0; i < n; i++) scanf("%d", &p[i]);  sort(p, p+n);      //排序,得到p的最小排列  do {    for(int i = 0; i < n; i++){printf("%d ", p[i]);     //输出排列p}printf("\n");  } while(next_permutation(p, p+n));  //求下一个排列  return 0; 
}

上述代码同样适用于可重集。
总结:枚举排列的常见方法有两种:一是递归枚举,二是用STL中的 next_permutation

7.3 子集生成

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线性表总结

线性表及其实现多项式的表示什么是线性表线性表的抽象数据类型描述线性表的顺序存储实现线性表的链式存储实现 线性表及其实现 多项式的表示 [例] 一元多项式及其运算 一元多项式 &#xff1a; 主要运算&#xff1a;多项式相加、相减、相乘等 【分析】如何表示多项式?…

mix2s android p功能,已升安卓P!网友:MIX2S才是亲儿子

原标题&#xff1a;已升安卓P&#xff01;网友&#xff1a;MIX2S才是亲儿子一直以来&#xff0c;小米在手机系统更新上都有着非常明显的优势&#xff0c;MIUI经过了多年的更新迭代&#xff0c;如今已经达到了非常不错的易用性&#xff0c;而且流畅度方面的表现更是优秀。如今小…

堆栈总结

堆栈什么是堆栈堆栈的抽象数据类型描述栈的顺序存储实现 堆栈 什么是堆栈 计算机如何进行表达式求值&#xff1f; 算术表达式56/2-3*4。 正确理解&#xff1a; 56/2-3*4 53-3*4 8-3*4 8-12 -4 由两类对象构成的&#xff1a; 运算数&#xff0c;如2、3、4 运算符号…

harmonyos公测招募,nova为主 HarmonyOS 2.0开发者Beta公测再招募

原标题&#xff1a;nova为主 HarmonyOS 2.0开发者Beta公测再招募HarmonyOS 2.0开发者Beta公测招募将开启第二期&#xff0c;本次公测活动主要针对的机型是华为nova系列。活动报名时间为5月9日-5月17日。【PChome手机频道资讯报道】华为方面在4月份开启了HarmonyOS 2.0开发者Bet…

队列总结

什么是队列 队列(Queue)&#xff1a;具有一定操作约束的线性表 插入和删除操作&#xff1a;只能在一端插入&#xff0c;而在另一端删除 数据插入&#xff1a;入队列&#xff08;AddQ&#xff09; 数据删除&#xff1a;出队列&#xff08;DeleteQ&#xff09; 先来先服务 先…

D P- 免费馅饼

题目 都说天上不会掉馅饼&#xff0c;但有一天gameboy正走在回家的小径上&#xff0c;忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了&#xff0c;这馅饼别处都不掉&#xff0c;就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了&#xff0c;所以ga…

一加桌面3.0 android8,一加手机XRemix6.0安卓8.1.0Beta2.0定制本地化增强适配归属农历等...

制作者&#xff1a;moonlight-roms基于版本&#xff1a;remix最新安卓8.1.0代码适合机型&#xff1a;一加手机X双网版/全网通版/E1001/E1003等/onyx注意事项&#xff1a;1.开机后语言设置&#xff1a;Settings-system-languageandinput-添加一个中文需要并拖动到第一行设置为默…

震惊!Fibonacci Again

题目 There are another kind of Fibonacci numbers: F(0) 7, F(1) 11, F(n) F(n-1) F(n-2) (n>2). Input Input consists of a sequence of lines, each containing an integer n. (n < 1,000,000). Output Print the word “yes” if 3 divide evenly into …

华为鸿蒙手机和电视通话,鸿蒙智慧屏首秀:逾10万人预定,电视视频通话功能强大...

原标题&#xff1a;鸿蒙智慧屏首秀&#xff1a;逾10万人预定&#xff0c;电视视频通话功能强大上周五&#xff0c;大家期待已经的华为开发者大会正式召开&#xff0c;华为鸿蒙系统在这一天对外发布。历时数年&#xff0c;经过几千人的研发&#xff0c;终于正式落地。曾经&#…

两文本一图片android,Android富文本编辑器(二):图文混排以及图片上传处理

对于一个富文本编辑器来说&#xff0c;图文混排是最基本的功能。而从上一篇文章中我们知道图文混排需要使用ImageSpan。下面这段代码摘自我的RichEditText源码&#xff1a;/*** 添加图片* param filePath 图片文件路径*/public void addImage(String filePath) {SpannableStrin…

震惊! Rightmost Digit 快速幂解决

题目 Given a positive integer N, you should output the most right digit of N^N. Input The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow. Each test case conta…

android handler同步,android解决:使用多线程和Handler同步更新UI

如果运行时&#xff0c;可以看到滚动条由条慢慢变短&#xff0c;则说明程序成功了。截图如下&#xff0c;建议选择大点的文件做测试。main.xmlxmlns:android"http://schemas.android.com/apk/res/android"android:layout_height"wrap_content" android:id&…

html玫瑰花效果代码,html5渲染3D玫瑰花情人节礼物js特效代码

情人节马上就要到来了&#xff0c;这里给程序员前端设计师们献上一个&#xff0c;html5渲染而成的3D玫瑰花js效果&#xff0c;可以作为虚拟的情人节礼物送给自己的爱人。支持html5的浏览器查看。查看演示下载资源&#xff1a;16次 下载资源下载积分&#xff1a;20积分情人节玫瑰…

html 页面怎么加载富文本,UILabel加载html富文本

本文主要解决html标签之外文本属性设置当APP里面有搜索的需求的时候&#xff0c;产品可能会要求关键字显示特殊颜色或者字体。其中一种可能性是服务器返回的数据是带有html标签的字符串&#xff0c;那么该怎么解决&#xff1f;当标签之外的其他字体也需要设置不同格式&#xff…

python 打印皮卡丘_用python打印你的宠物小精灵吧

我们来通过一个有趣的例子开始编写我们的第一个python代码。本文涉及的python基础语法为&#xff1a;print输出函数&#xff0c;赋值&#xff0c;字符串print()print()是python的一个内置函数&#xff0c;用于打印输出&#xff0c;是最常见的一个函数之一。有些朋友可能对于打印…

360浏览器查看html文件在哪里,360浏览器8菜单栏怎么弄出来?如何查看网站源代码...

今天想查看一个页面的源代码&#xff0c;没想到这个页面竟然禁止了鼠标右键功能&#xff0c;所以无法通过鼠标右键选择“查看源代码”来查看。然后就想通过 360 浏览器的“查看源代码”按钮进行查看&#xff0c;没想到找了半天都找不到这个按钮&#xff0c;最后只能通过查看源代…

判断html()中有长度,VBS 字符串长度判断的问题

对于给你一个字符串你应该怎样判断他的字符串长度&#xff0c;试举一些典型的例子C语言——字符串长度的计算方法提示&#xff1a;(1)计算字符串长度时关键是要注意辨认转义字符&#xff1b;(2)一个转义字符总是以反斜杠开始&#xff0c;再加一个其他字符组成。所以&#xff0c…

计算机专业后悔么,王言匀

《中华独特疗法大成》分方法和治疗两篇。方法篇依据用药与否&#xff0c;分为药物疗法和非药物疗法两章&#xff0c;药物疗法按照给药途径分为内服法和外用法两节&#xff1b;非药物疗法根据施术手段分为针灸、推拿、气功及其他4节。这些章节介绍了273种治疗方法的基本内容、适…

应用计算机散热的原理是什么,水冷散热器是什么原理?水冷可以带给机箱多大的散热作用?...

用户若是CPU安装了水冷散热器&#xff0c;就可以利用水快速导热和散热的特性加强主机硬件的散热效果&#xff0c;和普通风扇的散热效果相比&#xff0c;水冷可以更加有效的降低硬件温度和热量散发速度。阅读下文了解水冷散热器的原理和作用。CPU水冷散热器是指使用液体在泵的带…

计算机办公应用软件初级,电脑办公软件有哪些?桌面便签办公软件基础教程

原标题&#xff1a;电脑办公软件有哪些&#xff1f;桌面便签办公软件基础教程Windows电脑上的办公软件有很多&#xff0c;很多网友都觉得电脑上用的桌面便签小工具不算办公软件&#xff0c;其实只要是对工作有帮助的电脑程序&#xff0c;在某种程度上来说都可以说是办公小工具软…