算法专题-暴力枚举篇

Q1(uva 725):

  给出一个整数n,找到所有的0~9的排列,是的前五个数组成的整数能够整除后五个数组成的整数。

  分析:很典型的基本暴力枚举法,暴力求解往往伴随优化。这道题目的优化点在于枚举后五位得到10x9x8x7x6种情况,然后基于这些情况和等式关系,然后得到前面的整数,然后只需判断一下是否满足每个数字只出现了一次即可,由于题目是要求从小到大输出,这里枚举的时候控制一下从小到大枚举即可。

  参考代码如下:

 #include<cstdio>
#include<cstring>
using namespace std;

int main()
{


    int i , j , k , m , n;
    int visit[10];
    int x;
    int cas = 0;
    while(scanf("%d",&x) && x)
  {
      if(cas++) {printf("
");}
      int ok = 0;
      int flag = 0;
    for(int i = 0;i <= 9;i++)
    {

           for(j = 0;j <= 9;j++)
           {
                 if(i == j)  continue;
                 for(k = 0;k <= 9;k++)

                 {
                     if(k == i || k == j) continue;
                     for(m = 0;m <= 9;m++)
                     {
                           if(m == i || m == j || m == k)  continue;
                           for(n = 0;n <= 9;n++)
                             {
                                 memset(visit , 0 , sizeof(visit));
                                   if(n == i || n ==j || n == k || n == m) continue;
                                    int sum = 10000*i + 1000*j + 100*k + 10*m +n;
                                    if(x*sum >= 99999){flag = 1;break;}
                                    visit[i] = 1;visit[j] = 1;visit[k] = 1;visit[m] = 1;visit[n] = 1;
                                    int temp = x*sum;
                                    int nn = temp%10;temp/= 10;
                                    int mm = temp%10;temp/= 10;
                                    int kk = temp%10;temp/= 10;
                                    int jj = temp%10;temp/= 10;
                                    int ii = temp%10;
                                    visit[ii] = 1;visit[jj] = 1;visit[kk] = 1;visit[mm] = 1;visit[nn] = 1;
                                    int cnt = 0;
                                    for(int p = 0;p <= 9;p++)
                                           if(visit[p])  cnt++;

                                    if(cnt == 10)
                                         {printf("%d%d%d%d%d / %d%d%d%d%d = %d
",ii,jj,kk,mm,nn,i,j,k,m,n,x);ok=1;}

                             }
                             if(flag) break;
                     }
                     if(flag)  break;
                 }
                 if(flag) break;
           }
           if(flag)  break;
    }
    if(!ok)  printf("There are no solutions for %d.
",x);


  }
}

  Q2(hdu 1422):

  顺序给出1,2,3…n个城市的生活费和花费,他们组成一个环123…n1,现在你只能顺时针游览,请问你最多游览多少个城市?

  分析:这道题目本质上还是暴力,但是伪装了一层优化的分析,容易想到枚举开始的城市,然后遍历n个位置维护一个最大值即可,但是这样是个O(n^2)的时间复杂度,考察这题的数据量n最大是100000,显然这个方法会超时。

  进一步思考,假设我们当前的状态是从i出发,走到了城市j,在游览j-1城市的时候还没有被赶回家,游览第j个城市被赶回去了,按照我们上面枚举的方法,下次的状态是从i-1出发。但是其中的技巧在于,第一种状态如果存在,i城市的净收入一定为正,因此从i-1城市出发,到达j城市的净收入必然为负,还是要被赶回家,因此下面要枚举的量应该是第j+1个城市,之前那个状态游览的城市为j-i+1,将其存起来,维护一个最大值即可。

  参考代码如下:

 #include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 200000 + 5;
int a[maxn];
int main()
{
     int n;
     while(scanf("%d",&n) != EOF)
     {
            for(int i = 1;i <= n;i++)
            {
                  int x , y;
                  scanf("%d%d",&x,&y);
                  a[i] = x - y;
                  a[n+i] = x - y;
            }

  //for(int i = 1;i <= n;i++) printf("%d ",a[i]);
            int num = 0;
            int Max = 0;
            int ans = 0;
             for(int i = 1;i <= 2*n;i++)
             {
                 ans += a[i];
                 if(ans >= 0)
                      num++;
                 else
                 {
                       Max = max(num , Max);
                       //printf("%d
",Max);
                       num = 0 , ans = 0;
                 }

                Max = max(num , Max);
                 if(Max == n) break;


             }

             printf("%d
",Max);
     }
}

 Q3(最大乘积):

  给出n个元素组成的序列(n∈[1,18]),每个元素的绝对值大小不超过10,找到一个连续子序列,是的子序列各元素的乘积是最大的,输出这个最大值,如果最大值为负数,那么认为0是问题的答案。

  分析:连续子序列有两个要素,即起点和终点,记为有序对(x,y),考察数据大小,数据量较小,因此直接枚举即可维护最大值即可,这里记录最大值的变量最大可以达到10^18,因此用long long储存。

 #include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 20;

int main()
{
   int a[maxn];
     int n;
     int cas = 1;
     while(scanf("%d",&n) != EOF)
        {
              long long Max = 0;
              int ok = 0;
           for(int i = 0;i < n;i++)
               scanf("%d",&a[i]);

          for(int i = 0;i < n;i++)
               for(int j = i;j < n;j++)
              {
                      long long num = 1;

                          for(int k = i;k <=j;k++)
                              {num *= a[k];}
                 // printf("%d
",num);

                       if(num <= 0)   continue;
                       else           Max = max(Max , num);
                       // printf("%d%d
",i , j);
              }


                      printf("Case #%d: The maximum product is %lld.

",cas++,Max);

        }
}

  Q4(uva10976):

  输入正整数k,找到所有的整数对(x,y),满足x>y,且1/k = 1/x + 1/y.

  分析:这个题的点在于分分析出枚举对象的范围,等式有2个变元,我们枚举一个,容易利用等式关系求得另一个。这里利用不等关系x>y,容易推得1/k≤2/y,这样我们看到y的范围是[1,2k]。

  参考代码如下:

#include<cstdio>
using namespace std;


int main()
{
      int k;
      while(scanf("%d",&k) != EOF)
      {
          int cnt = 0;

                for(int y = 1;y <= 2*k;y++)
                {
                       if(y - k <= 0)  continue;

                       if((k*y%(y-k)) == 0)  cnt++;
                }
                printf("%d
",cnt);

               for(int y = 1;y <= 2*k;y++)
                {
                       if(y - k <= 0)  continue;

                       if((k*y%(y-k)) == 0)  {printf("1/%d = 1/%d + 1/%d
",k , k*y/(y-k) , y);}
                }
      }
}
原文地址:https://www.cnblogs.com/rhythmic/p/5980017.html