acm及蓝桥杯中的【暴力求解法】(1)(2.14)

 一、简单枚举

【例题1】:输入一个整数n,按从小到大的顺序输出所有形如abcde/fghij=n的表达式,其中a~j恰好为数字0~9的一个排列(可以有前导0),2<=n<=79。

代码如下:

#include
#include

bool vis[10];//【visit数组】用下标判断数字是否已经被访问过;
char str[15];//vis里的数字是按照0123456789的顺序,str是按照a和b给定的顺序

bool check(int a, int b)
{
    sprintf(str, "%05d%05d", a, b);

    //【格式化字符串】int sprintf( char *buffer, const char *format [, argument] ... )。除了前两个参数类型固定外,后面可以接任意多个参数,这取决于第二个参数需要几个后继参数。而它的精华,显然就在第二个参数:【格式化字符串】上。
    memset(vis, 0, sizeof(vis));

   //【memse】t:作用是在一段内存块中填充某个给定的值,它是对较大的结构体数组进行【清零操作】的一种最快方法。它有三个参数,一是所要set的首地址,二是set的值,三是set的字节数,vis肯定是个数组,因为数组在传参时能自动转成指向数组的首元素的指针,如果不是数组,应该写成memset(&vis, 0, sizeof(vis));
    for (int i = 0; i < 10; i++)
    {
        if (vis[str[i]-'0'])  return false;//如果已访问,返回false
        vis[str[i]-'0'] = true;//未访问,改变该数字对应的访问数组的值
    }
    return true;
 }

 int main()
 {
    int n;
    bool first = true;
    while (scanf("%d", &n) && n)
    {
        bool exist = false;
        if (first)  first = false;
        else  printf("\n");
        for (int i = 1234; i <= 98765; i++)

        { //这里设abcde为A,fghij为B。没有必要枚举0~9的所有排列,只需枚举B就可以算出A,然后判断是否所有数字          不相同即可。

            int ans;
            if (i % n == 0) //A可以整除n就说明这样的B存在,可以继续计算
            {
                ans = i / n;//计算出B;
                if (ans < 1234)  continue;//排除a<1234的情况;
                if (check(i, ans))//判断是否符合各个位不相同
                {
                    exist = true;
                    printf("%05d / %05d = %d\n", i, ans, n);//不足n位前补0:【%0nd】
                }
            }
        }
        if (!exist)  printf("There are no solutions for %d.\n", n);
    }
    return 0;
 }

总结:

1.暴力求解法并非是一味的暴力,本题中两个数之间有整除关系,只需确定一个;再利用每个数字都不同来筛选;

2.先找出满足整除条件的,然后筛掉不足1234的;

3.不会的知识点主要集中在【判断两个字符串中是否有重复数字】:

(1)格式化:用sprintf把两个数字格式化成一个字符串;

(2)memset函数的常用用法:对结构体或数组进行清零;

(3)visit数组记录该数字是否被访问过,一旦visit[str-‘0’]的值被改编为true,即可返回false,排除这一组答案。

4.所谓的暴力并不是全暴力,要适当的节省时间空间,其中可能还穿插着一些小技巧。

【例题2】:输入n个元素组成的序列S,你需要找出一个乘积最大的连续子序列。如果这个最大的乘积不是正数,应输出0(表示无解)。1<=n<=18,-10<=Si<=10。

分析:连续子序列--->联想到两要素:起点和终点--->只需枚举起点和终点。

代码如下:

#include
#include
using namespace std;

int main()
{
    int t,number,i,j,n,a[20];
    long long ans,max1;
    number=0;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
           scanf("%d",&a[i]);
        max1=0;
        for(i=1;i<=n;i++)
        {
            ans=a[i];
            if(ans>max1)
                max1=ans;
            for(j=i+1;j<=n;j++)
            {
                ans*=a[j];
                if(ans>max1) max1=ans;
            }
        }
        printf("Case #%d: The maximum product is %lld.\n\n",++number,max1);
    }
    return 0;
}

总结:

无论是求拥有连续最大和还是积的子序列,思想都是一样的。需要设定一个当前积ans,一个最大积max,第一重循环先固定第一个位置不动,把a[i]的值赋给ans,如果ans>max,就改变max的值为ans的值(也就是说,如果在这里改变了max的值,最大子序列的第一个位置就要发生变化)。嵌套在其中的第二重循环用于确定末位置,ans=ans*a[j],同样的,如果ans>max,就改变max的值为ans的值。

如果还要添加功能,即记录最大子序列的起始位置和终止位置,则还需要两个变量start,end......(未完待续)

你可能感兴趣的:(acm)