ACM暴力解题题解(一)

1.1163-XTUOJ

思路分析:
    看到这道题目,一理清题目要求,题目要我们做什么。二看存储结构,输入输出格式。初看这道题目,会有点不理解这道题目的输入,到底是直接在键盘上把样例敲进去,还是说这个样例其实占了三行呢?没关系,实践是检验真理的唯一标准,经过测试发现,如果是把样例直接敲进去而不按enter键,无法实现题目的输出,我们试着往第二种思路想,样例占了三行,说明这些文字是先存储好,然后进行操作,实验后发现果然是这样。在后续调试中发现,我们有一个ASCII码值为0A的字符没有输出,那么这个0A到底是什么呢?可以进行赋值输出验证呀,验证后发现什么都没有输出,能产生这个效果的也只有换行符了,于是果断往存储字符的数组中加上'\n'。最后得结果时,要特别注意题中的输出格式不可以在任何地方多空格或者换行,那么这个问题就迎刃而解了。
  十进制 十六进制
A 65 41
a 97 61
0 48 30
\n 10 0A

题目描述

给你一段ASCII编码的文字,输出其每个字符的ASCII码。

输入

一段文字,由ASCII码字符组成。

输出

先输出行号,行号为16进制,占5位,从0开始计数,行号前导为0,然后空一格。 每行最多输出32个字符的ASCII码,每个ASCII码为16进制,占2位,前导为0,中间用空格隔开。 所有16进制使用大写A~F表示10~15。最后一行行末无空格,无换行。

样例输入

ACM International Collegiate Programming Contest,
I LOVE YOU
Lotus is a mystic symbol. 

样例输出

00000 41 43 4D 20 49 6E 74 65 72 6E 61 74 69 6F 6E 61
00001 6C 20 43 6F 6C 6C 65 67 69 61 74 65 20 50 72 6F
00002 67 72 61 6D 6D 69 6E 67 20 43 6F 6E 74 65 73 74
00003 2C 0A 49 20 4C 4F 56 45 20 59 4F 55 0A 4C 6F 74
00004 75 73 20 69 73 20 61 20 6D 79 73 74 69 63 20 73
00005 79 6D 62 6F 6C 2E 20 0A
 
代码如下:
#include
using namespace std;
#define MAX  1000
typedef char SString[MAX];


int main()
{
    int colum=0;
    int k=0;
    SString str;
    SString str_rec;
    int flag=0;
    while(gets(str_rec))
    {
        int length_rec=strlen(str_rec);
        for(int i=0; i0) printf("\n");
            printf("%05X ",colum);//按格式输出
            colum++;
            k=0;
        }
      if(i==flag-1||k==15) printf("%02X",str[i]);按格式输出
      else printf("%02X ",str[i]);
      k++;
    }
}

2.1193-XTUOJ

Description

题目描述

Eason是个非常迷信的人,他喜欢数字3和6,不喜欢4和7。 如果一个数字的数码中没有4和7,而有3或者6的话,他就会喜欢这个数字。 比如,他会喜欢13,36,但是不会喜欢14,34。但对于28这种的,他就无所谓喜欢还是不喜欢。 Eason想知道区间[a,b]中一共有多少个他喜欢和不喜欢的数字?

输入

每行输入一个样例,为a和b,0≤a≤b≤106。如果a和b都为0,那么输入结束,这个样例不需要处理。

输出

每行输出一个样例的结果,先输出喜欢数字的个数,再输出不喜欢数字的个数。

样例输入

1 10 
1 100 
1 1000000 
0 0

样例输出

2 2 
28 36 
215488 737856
考点:
暴力解题的优化方法,gcd方法。

思路分析:
       审题后我们可以发现一些容易在编程时出错的地方。1.a,b数值范围较大,要考虑一下a,b的存储类型。2.暴力解题可能会超时。思考过后,我们可以先试着用暴力检索的方法,上传代码后发现,系统显示超时,这时候我们就必须从细节来分析,这个题目原先的算法有哪些可以改进的地方。在程序测试时,系统运行到得出结果的时间并不长,那么我们是否可以先把答案算出来,存在数组中,然后直接提取呢,按照这种思路,我们就可以避免每一次都要计算的问题。
       我们把num_like【i】定义为从1到i的数字中,有多少喜欢的数字,同理定义num_dislike【i】,那么最后的问题就是,如何求n-m中,喜欢和不喜欢的数字呢?我们先探求一下num_like[m]-num_like[n]这个值的物理意义,即数字n-m中,增加的喜欢的数字,如num_like[5]-num_like[2]=1,说明2-5中只增加了一个喜欢的数字。那么其他的例子如何呢?如num_like[5]-num_like[3]=0,这是怎么回事呢?经过比对发现,题中所求的是[n,m]区间的喜欢数字,而我们上述式子的意义确是增加的数字个数,没有把n,m放在其中。通过以上分析我们就可以得出结论,当n不为喜欢的数字时,增加的数字即为题中所求,当n为喜欢的数字时,必须把n加入其中,即加1,才为题中所求。同理可分析num_dislike[]。

易错点:
      书写gcd代码时,要注意到原式数据一直在变化,即i=i/10;后面要用到这个数据,则要把i的数据保存下来。
代码如下:
#include
using namespace std;
#define MAX 1000005
int num_like[MAX];
int num_dislike[MAX];
int num1;
int num2;
bool bool_like[MAX];
bool bool_dislike[MAX];
void i_judege(int i)
{
    int t=i;
    bool thr_ans,four_ans;
    thr_ans=four_ans=false;
    int temp;
    while(i!=0)
    {
        temp=i%10;
        i=i/10;
        if(temp==3||temp==6) thr_ans=true;
        if(temp==4||temp==7) four_ans=true;
    }//变量的命名和变量值的改变,gcd算法的易错地方
    if(thr_ans&&!four_ans)
    {
        num1++;
        bool_like[t]=true;
    }
    if(four_ans)
    {
        num2++;
        bool_dislike[t]=true;
    }
}
int main()
{
    int a,b;
    num1=num2=0;
    num_like[0]=num_dislike[0]=0;//忽略了这个样例
    num_like[1]=num_dislike[1]=0;
    memset(bool_like,false,sizeof(bool_like));
    memset(bool_dislike,false,sizeof(bool_dislike));
    for(int i=2; i<=1000000; i++)
    {
        i_judege(i);
        num_like[i]=num1;
        num_dislike[i]=num2;
    }
    while(scanf("%d%d",&a,&b),a,b)
    {
        int temp1;
        int temp2;
        if(bool_like[a])
        {
            temp1=num_like[b]-num_like[a]+1;
            temp2=num_dislike[b]-num_dislike[a];
        }
        if(bool_dislike[a])
        {
            temp1=num_like[b]-num_like[a];
            temp2=num_dislike[b]-num_dislike[a]+1;
        }
        if(!bool_like[a]&&!bool_dislike[a])
        {
            temp1=num_like[b]-num_like[a];
            temp2=num_dislike[b]-num_dislike[a];
        }
        printf("%d %d\n",temp1,temp2);
    }
    return 0;
}
3.1169-XTUOJ

题目描述

给你一个数列a1,a2,...,an,求m个连续数字组成的子段和最大值。

输入

有多个样例,每个样例的第一行是两个整数n和m,(1≤m≤n;≤100,000)。如果n和m为0表示输入结束,这个样例不需要处理。第二行是n个整数ai,0≤ai≤10000。

输出

每行输出一个整数,即样例的结果。

样例输入

6 3
1 2 3 4 5 6
6 3 
1 2 3 3 2 1
0 0

样例输出

15
8


考点:
暴力解题优化方法。

思路分析:
       分析题目,题目要求我们一个数列中,连续m个数字和的最大值。请注意,是一个数列,也就是说,这些数字的顺序已经被固定,不能在改变,这是第一个要注意到的细节。第二个细节,数字最大均达到了10e5的程度,暴力解题可能要注意一下会不会超时。还是由特殊到一般的思路。n=6,m=3,6个数字为1 2 3 3 2 1,我们来大概分析一下:
第一组:1+2+3
第二组:2+3+3
第三组:3+3+2
第四组:3+2+1
观察上述的数据求和,第一组中的2+3在第二组中出现,而第三组的3+2同样在第四组出现。说明,我们可以保存下中间的值,删增求和的两边即可。

易错点:
最终结果和中间值必须分开存放。
代码如下:
#include
using namespace std;
#define MAX 100005

int num[MAX];

int main()
{
    int m,n;
    while((scanf("%d%d",&n,&m),n,m))
    {
        int result=0,ans=0;
        for(int i=0;i





你可能感兴趣的:(ACM经验之谈)