ACM做题过程中的一些小技巧

1.一般用C语言节约空间,要用C++库函数或STL时才用C++;

cout、cin和printf、scanf最好不要混用。

2.有时候int型不够用,可以用long long或__int64型(两个下划线__)。

值类型表示值介于 -2^63 ( -9,223,372,036,854,775,808) 到2^63-1(+9,223,372,036,854,775,807 )之间的整数。

printf("%I64d",a);

printf("%lld",a);

3.OJ判断是只看输出结果的。

所以大部分题处理一组数据后可以直接输出,就不需要用数组保存每一个Case的数据。

while(case--)

{scanf(...);

......

printf(...);

}

4.纯字符串用puts()输出。

数据大时最好用scanf()、printf()减少时间。

先用scanf(),再用gets()会读入回车。

scanf("%c%c",&c1,&c2)会读入空格;

5. 读到文件的结尾,程序自动结束

while( ( scanf(“%d”,&a) ) != -1 )

while( ( scanf(“%d”,&a) ) != EOF)

while( ( scanf(“%d”,&a) ) == 1 )

读到一个0时,程序结束

while( scanf(“%d”,&a) &&a)

读到多个0时,程序结束

while( scanf(“%d%d%d”,&a,&b,&c)&&a+b+c )

6.数组定义int a[10]={0};可以对其全部元素赋值为0;

全局变量,静态变量自动初始化为0;

7.有很多数学题是有规律的,直接推公式或用递归、循环。

8.圆周率=cos(0.0)

自然对数=exp(1.0)

9.如果要乘或除2^n,用位移运算速度快。a>>n;a<<n;

10.定义数组时,数组大小最好比告诉的最大范围大一点。字符数组大小必须比字符串最大长度大1。处理字符数组时不要忘了在最后加'\0'。

11.擅用三目运算符

int max(int a,int b)

{return a>b?a:b;

}

int gcd(int m,int n)

{return n?gcd(n,m%n):m;

}

 int abs(int a)

{return a<0?-a:a;

}

12.将乘法转换成加法减少时间

log(a*b)=log(a)+log(b)

将乘法转换成除法防止溢出

a/(b*c)=a/b/c

13.排序要求不高时可以用C++的STL模板函数sort(),stable_sort()

int a[n]={...};

sort(a,a+n);

bool cmp(int m,int n)

{return m>n;

}

sort(a,a+n,cmp);

14.有的题数据范围小但是计算量大可以用打表法

先把结果算出来保存在数组里,要用时直接取出来。

1.输入输出

ACM和TopCoder不同,TopCoder只用让参赛者写一个class,而ACM需要参赛者完成整个console程序.在TopCoder中,输入输出是通过parameter传递的,不用过多考虑,在ACM中,却需要自己编写.

(1).只有一组输入时,这种情况不用我说了,都会,但是通常也不会有这么水的题

(2).固定组数的输入,可以利用一个计数器和while语句完成,

01 #include <iostream>
02 
03 int main(void){
04     int n;
05     scanf("%d", &n);
06     while (n--){
07         //...
08     }
09     //...
10     return 0;
11 }

(3).测试数据结尾有标志字符(例如最后一组输入后给一个0),这个只用加一个if语句判断读入的数据是什么,是结束标志跳出就ok了.也不多说了

(4).题目没有告诉你有多少组数据,这个通常是最令新手疑惑的,这种情况,一般用文件结束标志EOF判断

01 #include <iostream> 
02 
03 int main(void){
04     int n;
05     while (scanf("%d", &n) != EOF){
06     //...
07     } 
08     //...
09     return 0;
10 }

其实这里也可以用c++的cin输入流判断,例如

01 #include <iostream>
02 
03 using namespace std;
04 
05 int main(void){
06     int n;
07     while (cin>>n){
08     //...
09     } 
10     //...
11     return 0;
12 }

但是这样不是特别好,为什么?下面会说.

对于输出,最好采用的接收一组数据,处理一组数据,把结果保存在一个缓冲数组中,待所有输入结束后,再一起输出,而不是待接收完所有输入后,再处理,再输出,这样会消耗更多的memory,而且会更慢.

2.关于效率

第一,上面的所有例子,均采用的c标准I/O,为什么不用c++的cin,cout呢?是有原因的,经实践,在大规模输入输出下,cin,cout效率远远低于scanf()和printf(),原因据我估计应该是以为scanf(),printf()是汇编写的(这点可以从这两个函数均可以接受任意多组parameter(s)看出,c/c++函数是不具备这样的性质的),而cin,cout均是直接c/c++写的流操作,本来c/c++就比汇编慢,还引入流,所以自然比scanf(),printf()慢了.因此,在输入输出数据量很小的情况下,出于方便的原因,可以采用cin,cout,而在输入输出数据量比较大的情况下用scanf(),printf()比较保险,避免超时.

第二.ACM中,除了c/c++,一般还支持java等语言,但是由于java是解释执行的,效率十分低下,为此,一般的JudgeOnline都把java的time limit设置为题目给定值(也就是c/c++的time limit)的三倍,而且给每一组输入再额外提供150ms.即使是这样,java遇上复杂或者高精度计算的题目,还是很容易超时,因为效率有时候还远远未到c/c++的1/3.因此,一般来说,除了个别java极其有利的情况(例如字符串处理),不建议使用java.

3.关于调试

(1)调试的时候可以使用重定向,从文件中读入数据、直接将答案输出到文件中。

例如:

       以读的方式打开输入文件:freopen(“txtin.txt”,”r”,stdin);

       以写的方式打开输出文件:freopen(“txtin.txt”,”w”,stdout);

但是需要注意的是,再提交代码的时候一定要记得将以上两句与文件操作有关的代码去掉,否在系统会因找不到对应的文件而出错。

(2)题目一般给出的测试数据都是较少的,为了验证程序的正确性,要自己多设几组测试数据,尤其不要忘记了边界值的处理,边界往往会是容易出错的地方。

(3)再写计算公式的时候,若等式两边的类型不一样要特别注意类型转换,否常常会得到一个错误的结果。比如,int a,b; double c;

c=a/b;若a可以整除b则答案是正确的,否在a/b的结果只取结果的整数部分,并不会自动转换为double类型。正确的写法应该是c=(double)a/b,这样才会得到正确的答案。

还有再调用一些数学函数的时候,比如floor(),应该注意的是其参数是double型的,再调用时应该写为floor((double)a/b);否在a/b自动取整,floor函数并不起作用。

 

 

 

由于ACM竞赛题目的输入数据和输出数据一般有多组(不定),并且格式多种多样,所以,如何处理题目的输入输出是对编程者最基本的要求。

(一)第一类输入:输入不说明有多少个Input Block,以EOF为结束标志。

Example1:

Calculate A + B .

Input:Each line will contain two integers A and B . Process to end of file.

Output:For each case, output A + B in one line.

Sample Input
1 1
2 2

Sample Output
2
4

这个例子的意思是要每读取两个数就将两个数的和进行输出,输入的解决方法如下:

C语法:

while(scanf("%d %d",&a, &b) != EOF)


//放入计算和的语句即可
}

C++语法:

while( cin >> a >> b ) 

    //放入计算和的语句即可
}

注:scanf函数返回值就是读出的变量个数,如:scanf( “%d  %d”, &a, &b ); 
如果只有一个整数输入,返回值是1,如果有两个整数输入,返回值是2,如果一个都没有,则返回值是-1。EOF是一个预定义的常量,等于-1。

在有的比赛场合,测试数据的并不是由后台通过cin和cout输入而是要去读文件,这里就需要了解和掌握C++读写文件的操作。在C++中对一个文件进行读写需要先打开一个文件,然后使用fin和fout进行读写,在完成所有数据的读写操作后要关闭文件。下面是C和C++读写文件的操作,还是以上面计算a+b为例,看看C++和C是怎样操作文件流的。

C++语法:

#include<iostream>

#include<fstream>

using namespace std;

void main()

{

    ifstream fin("in.txt");

    ofstream fout("out.txt");

    int a,b;

    while(fin>>a>>b)

        fout<<a+b<<endl;

    fin.close();

    fout.close();

}

C语法:

#include <stdio.h>

void main()

{

    FILE *fin,*fout;

    int a,b;

    fin=fopen("in.txt","r");

    fout=fopen("out.txt","w");

    while(fscanf(fin,"%d %d",&a, &b) != EOF)

        fprintf(fout,"%d\n",a+b);

    fclose(fin);

    fclose(fout);

}

(二)第二类输入:题目告诉有N个Input Block,下面接着是N个Input Block。

Example2:

Problem Description

Your task is to Calculate a + b.
Input:

Input contains an integer N in the first line, and then N lines follow. Each line consists of a pair of integers a and b, separated by a space, one pair of integers per line. 
Output:

For each pair of input integers a and b you should output the sum of a and b in one line, and with one line of output for each line in input. 
Sample Input:

2

1 5

10 20 

Sample Output:

6

30

在这个题目中明确告知了输入数据有2组,然后每一组代表需要计算和的两个加数,因此只需要进行两次循环即可。这种类型的输入数据方式如下:

C语法:

scanf("%d",&n) ;

for( i=0 ; i<n ; i++ ) 

    scanf("%d %d",&a, &b);

   printf(“%d\n”,a+b);
}

C++语法:

cin >> n; 
for( i=0 ; i<n ; i++ ) 

    cin>>a>>b;

   cout<<a+b<<endl;
}

(三)第三类输入:输入不说明有多少个Input Block,但以某个特殊输入为结束标志。

Example3:

Problem Description

Your task is to Calculate a + b.

 Input

Input contains multiple test cases. Each test case contains a pair of integers a and b, one pair of integers per line. A test case containing 0 0 terminates the input and this test case is not to be processed.
Output

For each pair of input integers a and b you should output the sum of a and b in one line, and with one line of output for each line in input. 
Sample Input

1 5

10 20

0 0 
Sample Output

6

30

这里题目中也是计算a和b的和,每次读取两个数,然后输出和,但是至于有多少组数据需要输入则是由特殊的输入确定,在该题目中当读入的两个数都为零时,则认为数据的输入结束。

C语法:

#include <stdio.h>

int main()

{

    int a,b;

    while(scanf("%d %d",&a, &b) &&(a!=0 && b!=0))

        printf("%d\n",a+b);

}

C++语法:

#include<iostream>

using namespace std;

int main()

{

    int a,b;

    while(cin>>a>>b)

    {

        if(a==0 && b==0)

            break;

        cout<<a+b<<endl;

    }

    return 0;

}

对于第三类输入输出,通常的解决方案如下,至于遇到何种结束方式,这根具体的题目有关,但是通常此类输入解法的方法如下,只需要根据具体的结束规则稍加改动即可。

C语法:

while(scanf("%d",&n) !=EOF && n!=0 )


}

 

C++语法:

while( cin >> n && n != 0 ) 

}

(四)第四类输入:上述三种的组合

Example4:

Your task is to Calculate the sum of some integers.
Input:

Input contains multiple test cases. Each test case contains a integer N, and then N integers follow in the same line. A test case starting with 0 terminates the input and this test case is not to be processed.
Output:

For each group of input integers you should output their sum in one line, and with one line of output for each line in input. 
Sample Input:

4 1 2 3 4

5 1 2 3 4 5

0  
Sample Output

10

15

这个题目同样是计算a+b的和,但是数据的输入格式是这样的,先输入N,代表后面有N个数据进行相加,当N为0时,表示输入结束。因此,本题目中的输入格式如下:

#include<iostream>

using namespace std;

int main()

{

    int i,n,s,sum;

    while(cin>>n && n!=0)

    {

        sum=0;

        for(i=0; i<n; i++)

        {

            cin>>a;

            sum+=a;

        }

        cout<<sum<<endl;

    }

    return 0;

}

(五)第五类输入:输入是一整行的字符串,本类的输入方式如下:

C语法:

char buf[20]; 
gets(buf);

C++语法:

char buf[ 255 ];
cin.getline( buf, 255 );

注:在C中canf(“%s%s”,str1,str2),在多个字符串之间用一个或多个空格分隔;

若使用gets函数,应为gets(str1); gets(str2); 字符串之间用回车符作分隔。在通常情况下,接受短字符用scanf函数,接受长字符用gets函数。getchar函数每次只接受一个字符,经常c=getchar()这样来使用。在C++中读入字符串通常使用cin.getline函数,可以接受用户的输入的字符,直到已达指定个数,或者用户输入了特定的字符。它的函数声明形式(函数原型)如下:

istream& getline(char line[], int size, char endchar = '\n');

char line[]: 就是一个字符数组,用户输入的内容将存入在该数组内。

int size : 最多接受几个字符,用户超过size的输入都将不被接受。

char endchar :当用户输入endchar指定的字符时,自动结束,默认是回车符。

(六)第一类输出:一个Input Block对应一个Output Block,Output Block之间没有空行。

Example5:

Sample Input:

1 5

10 20

Sample Output:

6

30

这里我们主要关心输出格式,同样是计算a+b的和,要求没输出两个数的和就要求输出一个换行。本类的输出方式如下:

C语法:

{

//计算语句
  printf("%d\n",ans);

}

C++语法:

{

//计算语句
cout << ans << endl; 
}

(七)第二类输出:一个Input Block对应一个Output Block,每个Output Block之后都有空行。

Example6:

Sample Input:

1 5

10 20

Sample Output:

6

(这里有空行)

30

(这里有空行)

这里的输出方式很上面一类的输出方式唯一不同的是,每一组数据结果输出后跟了两次换行,第一换行是将输出光标移到下一行的输出其实位置,第二次换行是为了增加一个空行,让输出光标的位置再次移动到下一行的其实位置。

本类的输出方式如下:

C语法:

{

//计算语句
   printf("%d\n\n",ans);

}

C++语法:

{

//计算语句
    cout << ans << endl << endl; 
}

(八)第三类输出:一个Input Block对应一个Output Block,每个Output Block之后都有空行。注意:这里是跟第二类输出有所区别,第二类四每一个输出块后紧跟一个空行,这里是输出块之间有空行,意思就是说最后一个输出块之后是没有空行的。当然,在实际的编程中,除非明确知道有多少个输入块,否则最后一个空行确实不太好控制,当然要是算法题目中没有明确告知有多少个输入块,仍旧采用本类输出方式,一般对最后一个多余的空行是不做格式检查的。

Example7: 
Sample Input:

3

4 1 2 3 4

5 1 2 3 4 5

3 1 2 3
Sample Output:

10

(这里有空行)

15

(这里有空行)

6(这里没有空行)

本类的输出方式如下:

C语法:

scanf(“%d”,&count);

for (k=0;k<count;k++) 

      while (…) 
      { 
             printf(" %d\n",result); 
      } 
      if (k!=count-1) printf("\n"); 
}

C++语法:

类似,输出语句换一下即可。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM的,你懂得 ACM过程的一些小技巧。 1.一般用C语言节约空间,要用C++库函数或STL时才用C++; cout、cin和printf、scanf最好不要混用。 大数据输入输出时最好不要用cin、cout,防止超时。 2.有时候int型不够用,可以用long long或__int64型(两个下划线__)。 值类型表示值介于 -2^63 ( -9,223,372,036,854,775,808) 到2^63-1(+9,223,372,036,854,775,807 )之间的整数。 printf("%I64d",a); //__int64 一般VC编译器使用(虽然有的OJ用g++,但是动态链接库用的windows的,所以要用%I64d输入输出) printf("%lld",a); //long long 一般g++编译器使用 3.OJ判断是只看输出结果的,所以不要要多余的提示输出。 所以大部分处理一组数据后可以直接输出,就不需要用数组保存每一个Case的数据。 while(case--) { scanf(...); ...... printf(...); } 4.纯字符串用puts()输出。 数据大时最好用scanf()、printf()减少时间。 先用scanf(),再用gets()会读入回车。所以在间加一个getchar(); scanf("%c%c",&c1,&c2)会读入空格;建议用%s读取字符串,取第一个字符。 5. 读到文件的结尾,程序自动结束 while( ( scanf(“%d”, &a) ) != -1 ) while( ( scanf(“%d”, &a) ) != EOF) while( ( scanf(“%d”, &a) ) == 1 ) while( ~( scanf(“%d”, &a) ) ) 读到一个0时,程序结束 while( scanf(“%d”, &a) , a) while( scanf(“%d”, &a)!=EOF && a) 读到多个0时,程序结束 while( scanf(“%d%d%d”, &a, &b, &c), a+b+c ) //a,b,c非负 while( scanf(“%d%d%d”, &a, &b, &c), a|b|c ) 6.数组定义int a[10] = {0};可以对其全部元素赋值为0; 数组太大不要这样,防止CE。 全局变量,静态变量自动初始化为0; 函数定义的变量存储在栈空间,数组太大需要定义为全局变量(存储在堆空间)。 7.有很多数学是有规律的,直接推公式或用递归、循环。 8.圆周率=acos(-1.0) 自然对数=exp(1.0) 9.如果要乘或除2^n,用位移运算速度快。a>>n;a<b?a:b; } int gcd(int m,int n) { return n?gcd(n,m%n):m; } int abs(int a) { return an; } sort(a,a+n,cmp); 14.有的数据范围小但是计算量大可以用打表法 先把结果算出来保存在数组里,要用时直接取出来。 15.浮点数比较时最好控制精度 #define eps 1e-6 fabs(a-b)<eps 16.有些字符串与整型的转换函数是非标准的 可以使用sscanf()和sprintf()代替 sscanf(s,"%d",&n);//从字符串s读入整数n sprintf(s,"%d",n);//将n转换为字符串s

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值