[关闭]
@Moritz 2019-03-26T04:26:10.000000Z 字数 6452 阅读 469

第七章 暴力求解法

aoapc_2nd C++ 所有文稿


7.1 简单枚举

例题 7-1 除法

输入正整数n,从小到大的顺序输出所有形如abcde/fghij=n的表达式,其中a~j恰好为数字0-9的一个排列。

思路:即使参考了书上的思路,看上去很容易实现,实际上真正上手写出来代码花了几个多小时,再次深刻地体现了现在纸上写好细节思路的重要性,以及对应功能用函数实现使到代码简化的必要性。

  1. /*19:48*/
  2. /*21:04*/
  3. #include <iostream>
  4. #include <cmath>
  5. using namespace std;
  6. int a[20],n,cnt=0;
  7. int turn_5(int x=1,int y=5){//把除数(数组的前五个数)化成整数
  8. int tot=0;
  9. float e=0.0;
  10. for(int i=x;i<=y;i++){
  11. tot=tot+a[i]*pow(10,e);
  12. //printf("i=%d tot=%d\n",i,tot);
  13. e=e+1.0;
  14. }
  15. return tot;
  16. }
  17. int devi(int x,int n,int a=5){//把被除数拆成5个数字,以便判重
  18. int e=1;
  19. for(int i=1;i<n;i++) e*=10;
  20. x/=e;
  21. x=x%10;
  22. return x;
  23. }
  24. void find_5(int cur){//生成除数的所有可能
  25. /*递归边界,判重*/
  26. if (cur>5) {
  27. if (turn_5()*n<=99999) {
  28. bool ok=true;
  29. int x=turn_5()*n;
  30. for(int i=6;i<=10;i++){
  31. a[i]=devi(x,i-5,5);
  32. for(int j=1;j<i;j++){
  33. if (a[j]==a[i]){
  34. //printf("turn_5=%d x=%d a[%d]=a[%d]==%d\n",turn_5(),x,i,j,a[i]);
  35. ok=false;
  36. break;
  37. }
  38. }
  39. }
  40. if (ok){//如果不重复 输出
  41. if (cnt++)
  42. cout<<endl;
  43. for(int i=10;i>=6;i--) cout<<a[i];
  44. cout<<'\\';
  45. for(int i=5;i>=1;i--) cout<<a[i];
  46. cout<<'\='<<n;
  47. }
  48. }
  49. return;
  50. }
  51. /*生成除数的排列*/
  52. for(int i=0;i<=9;i++){
  53. bool ok=true;
  54. for(int j=1;j<cur;j++){
  55. if (a[j]==i){
  56. ok=false;
  57. break;
  58. }
  59. }
  60. if (ok){
  61. a[cur]=i;
  62. find_5(cur+1);
  63. }
  64. }
  65. }
  66. int main(){
  67. cin>>n;
  68. memset(a,0,sizeof(a));
  69. find_5(1);
  70. system("pause");
  71. return 0;
  72. }

出现的几个难题(自己挖的坑):


例题 7-2 最大乘积

输入n个元素组成的序列S,找出一个乘积最大的连续子序列,如果最大的乘积不是正数,则输出0。(1<=n<=10,-10<=Si<=10)

思路:直接循环枚举,没啥难度,反而一开始还想多了,开始写solve递归函数……

  1. /*21:32*/
  2. /*21:44*/
  3. #include <iostream>
  4. using namespace std;
  5. long long max_mu=0;
  6. int a[20],n;
  7. int mu(int x,int y){
  8. int tot=1;
  9. for(int i=x;i<=y;i++) tot*=a[i];
  10. return tot;
  11. }
  12. int main(){
  13. int en=0;
  14. while(cin>>n){
  15. memset(a,0,sizeof(a));
  16. max_mu=0;
  17. for(int i=0;i<n;i++) cin>>a[i];
  18. for(int i=0;i<n-1;i++){
  19. for(int j=1;j<n;j++){
  20. if (j-i>=1){
  21. max_mu=(max_mu>mu(i,j)?max_mu:mu(i,j));
  22. }
  23. }
  24. }
  25. if(en++) cout<<endl;
  26. cout<<max_mu;
  27. }
  28. system("pause");
  29. return 0;
  30. }

7.2 枚举排列

7.2.1 生成1~n的排列

写就是了

  1. /*21:49*/
  2. /*21:57*/
  3. #include <iostream>
  4. using namespace std;
  5. const int maxn=1000;
  6. int a[maxn],n;
  7. void print_per(int cur){
  8. if (cur>=n){
  9. cout<<"(";
  10. int co=0;
  11. for(int i=0;i<n;i++){
  12. if (co++) cout<<",";
  13. cout<<a[i];
  14. }
  15. cout<<")"<<endl;
  16. return;
  17. }
  18. for(int i=1;i<=n;i++){
  19. bool ok=true;
  20. for(int j=0;j<cur;j++){
  21. if (a[j]==i){
  22. ok=false;
  23. break;
  24. }
  25. }
  26. if(ok){
  27. a[cur]=i;
  28. print_per(cur+1);
  29. }
  30. }
  31. }
  32. int main(){
  33. cin>>n;
  34. print_per(0);
  35. system("pause");
  36. return 0;
  37. }

7.2.2 生成可重集的排列

  1. /*22:04*/
  2. /*22:23*/
  3. #include <iostream>
  4. #include <algorithm>
  5. using namespace std;
  6. const int maxn=1000;
  7. int a[maxn],n,p[maxn],re[10];
  8. int count(int x,int cur){//优化
  9. int tot=0;
  10. for(int i=0;i<cur;i++){
  11. if (a[i]==x) tot++;}
  12. return tot;
  13. }
  14. void print_per(int cur){
  15. if (cur>=n){
  16. cout<<"(";
  17. int co=0;
  18. for(int i=0;i<n;i++){
  19. if (co++) cout<<",";
  20. cout<<a[i];
  21. }
  22. cout<<")"<<endl;
  23. return;
  24. }
  25. for(int i=0;i<=n;i++){//从p0-pn取值
  26. if (i==0||p[i]!=p[i-1]){//注意!!!:此举避免了出现重复的排列,例如(1,1,1)的情况
  27. if (count(p[i],cur)<re[p[i]]){//如果现有a元素中p元素各值的数量未满
  28. a[cur]=p[i];
  29. print_per(cur+1);
  30. }
  31. }
  32. }
  33. }
  34. int main(){
  35. cin>>n;
  36. memset(p,0,sizeof(p));
  37. memset(a,0,sizeof(a));
  38. memset(re,0,sizeof(re));
  39. for(int i=0;i<n;i++){
  40. cin>>p[i];
  41. re[p[i]]++;//优化书中代码,只需统计一次p中p[i]出现的次数
  42. }
  43. sort(p,p+n);
  44. print_per(0);
  45. system("pause");
  46. return 0;
  47. }

7.3 子集生成

看了下,没怎么管。 -2019.3.8


7.4 回溯法

7.4.1 八皇后问题

看完分析之后试写了一下

  1. /*10:14*/
  2. /*10:58*/
  3. #include <iostream>
  4. #include <cstdio>
  5. #include <set>
  6. #include <map>
  7. #include <string>
  8. #include <utility>
  9. using namespace std;
  10. int a1[20],a2[20],cnt=0;
  11. pair<int,int> point[10];//存放皇后的坐标
  12. map<set<pair<int,int> > ,int> m;
  13. bool an1(int x,int y,int cur){//判断(x,y)点主对角线上是否有皇后
  14. /*if (a1[y-x+7]==0) return true;
  15. return false;*/
  16. for(int i=1;i<cur;i++){
  17. if (point[i].first+point[i].second==x+y) return false;
  18. }
  19. return true;
  20. }
  21. bool an2(int x,int y,int cur){//判断的对角线上是否有皇后
  22. /*if (a2[y+x]==0) return true;
  23. return false;*/
  24. for(int i=1;i<cur;i++){
  25. if (point[i].first-point[i].second==x-y) return false;
  26. }
  27. return true;
  28. }
  29. void search(int cur){
  30. if (cur>8){
  31. set<pair<int,int>> p;
  32. for(int i=1;i<=8;i++){
  33. p.insert(make_pair((point[i]).first,(point[i]).second));
  34. }
  35. if (!m.count(p)){
  36. m[p]=1;
  37. printf("Case %d\n",++cnt);
  38. for(int i=1;i<=8;i++){
  39. printf("Point %d:(%d,%d)\n",i,point[i].first,point[i].second);
  40. }
  41. }
  42. return;
  43. }
  44. for(int x=1;x<=8;x++){//确定x坐标
  45. bool ok_x=true;
  46. for(int i=1;i<cur;i++){//y坐标判重
  47. if ((point[i]).first==x){
  48. ok_x=false;
  49. break;
  50. }
  51. }
  52. if (ok_x){
  53. for(int y=1;y<=8;y++){
  54. bool ok_y=true;
  55. for(int j=1;j<cur;j++){//x坐标判重
  56. if ((point[j]).second==y){
  57. ok_y=false;
  58. break;
  59. }
  60. }
  61. if (ok_y){
  62. if (an1(x,y,cur)&&an2(x,y,cur)){
  63. point[cur].first=x;
  64. point[cur].second=y;
  65. search(cur+1);
  66. }}}}}}
  67. int main(){
  68. memset(a1,0,sizeof(a1));
  69. memset(a2,0,sizeof(a2));
  70. memset(point,0,sizeof(point));
  71. search(1);
  72. system("pause");
  73. return 0;
  74. }
  75. /*
  76. (输出的点和坐标已省略,共92组解
  77. Process returned 0 (0x0) execution time : 57.719 s
  78. Press any key to continue.
  79. */

没有意识到,其实直接规定每行有一个皇后就可以了,不用进行y坐标的判重,算法复杂度明显提高,运行时间将近一分钟,并且还需要运用map进行判重。

优化之后:

  1. /*2.1 去掉了map判重*/
  2. #include <iostream>
  3. #include <cstdio>
  4. #include <string.h>
  5. #include <utility>
  6. using namespace std;
  7. int cnt=0;
  8. pair<int,int> point[10];//存放皇后的坐标
  9. bool an1(int x,int y,int cur){//判断(x,y)点主对角线上是否有皇后
  10. for(int i=1;i<cur;i++){
  11. if (point[i].first+point[i].second==x+y) return false;
  12. }
  13. return true;
  14. }
  15. bool an2(int x,int y,int cur){//判断的对角线上是否有皇后
  16. for(int i=1;i<cur;i++){
  17. if (point[i].first-point[i].second==x-y) return false;
  18. }
  19. return true;
  20. }
  21. void search(int cur){
  22. if (cur>8){
  23. printf("Case %d\n",++cnt);
  24. for(int i=1;i<=8;i++){
  25. printf("Point %d:(%d,%d)\n",i,point[i].first,point[i].second);
  26. }
  27. return;
  28. }
  29. for(int y=1;y<=8;y++){
  30. bool ok_y=true;
  31. for(int j=1;j<cur;j++){//x坐标判重
  32. if ((point[j]).second==y){
  33. ok_y=false;
  34. break;
  35. }
  36. }
  37. if (ok_y){
  38. if (an1(cur,y,cur)&&an2(cur,y,cur)){
  39. point[cur].first=cur;
  40. point[cur].second=y;
  41. search(cur+1);
  42. }
  43. }
  44. }
  45. }
  46. int main(){
  47. memset(point,0,sizeof(point));
  48. search(1);
  49. system("pause");
  50. return 0;
  51. }
  52. /*至此,完成,11:11
  53. Process returned 0 (0x0) execution time : 0.156 s
  54. Press any key to continue.
  55. */

7.4.2 其他应用举例

例题 7-5 困难的串

一开始理解出现偏差,误以为第k小的困难串长度等于k,输入为30 3的时候输出错误,然后修改得乱七八糟

  1. /*10:27*/
  2. #include <iostream>
  3. #include <string>
  4. using namespace std;
  5. int n,l;
  6. string str(80,'0'),str2(80,'0');//string初始化,否则str[cur-1]=c语句会造成溢出
  7. void solve(int cur){
  8. if (cur>n){
  9. cout<<str<<endl;
  10. system("pause");
  11. return;
  12. }
  13. else{
  14. for(int i=0;i<l;i++){
  15. char c='A'+i;
  16. bool ok=true;
  17. cout<<"enter loop cur="<<cur<<endl;
  18. for(int len=1;len<cur/2;len++){
  19. if (str.substr(cur-len)==str.substr(cur-len*2,len)){
  20. cout<<str.substr(cur-len)<<endl;
  21. cout<<str.substr(cur-len*2,len)<<endl;
  22. cout<<str<<endl;
  23. ok=false;
  24. break;
  25. }
  26. }
  27. cout<<"loop ok cur="<<cur<<endl;
  28. if (ok){
  29. str[cur-1]=c;
  30. solve(cur+1);
  31. }
  32. }
  33. }
  34. }
  35. int main(){
  36. cin>>n>>l;
  37. //solve(1);
  38. int cur=1,cnt=0;
  39. while(cnt<=n){
  40. bool ok=true;
  41. char c;
  42. for(int i=0;i<l;i++){
  43. c='A'+i;
  44. str2[cur-1]=c;
  45. //cout<<"enter loop cur="<<cur<<endl;
  46. ok=true;
  47. for(int len=1;len<=cur/2;len++){
  48. //cout<<str.substr(cur-len,len)<<endl;
  49. //cout<<str.substr(cur-len*2,len)<<endl;
  50. if (str2.substr(cur-len,len)==str2.substr(cur-len*2,len)){
  51. //cout<<str.substr(cur-len,len)<<endl;
  52. //cout<<str.substr(cur-len*2,len)<<endl;
  53. ok=false;
  54. break;
  55. }
  56. }
  57. if (ok) {
  58. //cout<<"loop ok cur="<<cur<<endl;
  59. cnt++;
  60. str[cur-1]=c;
  61. str2[cur-1]=c;
  62. }
  63. }
  64. cur++;
  65. }
  66. for(int i=0;i<cur;i++) cout<<str[i];
  67. system("pause");
  68. return 0;
  69. }
  70. /*运行结果:
  71. 7 3
  72. CBBB0请按任意键继续. . .
  73. 不改了,看书*/

书上照搬,发现30 3无输出

挖个坑先


例题 7-6 带宽


2019.3.8

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注