ACM教程 - 基础篇

  1. C/C++ 基础知识篇:点击打开链接
  2. ACM IO 模板(控制台版):
    #include<bits/stdc++.h>
    #include<cmath>
    
    #define mem(a,b) memset(a,b,sizeof a)
    #define ssclr(ss) ss.clear(), ss.str("")
    #define INF 0x3f3f3f3f
    #define MOD 1000000007
    
    using namespace std;
    
    typedef long long ll;
    
    int main()
    {
    //    int T; scanf("%d",&T);
    //    int n;
    //    while(T-- && ~scanf("%d",&n))
    //    {
        int n;
        while(~scanf("%d",&n))
        {
    
        }
    
        return 0;
    }

  3. ACM IO 模板(文件版):
    #include<bits/stdc++.h>
    #include<cmath>
    
    #define mem(a,b) memset(a,b,sizeof a)
    #define ssclr(ss) ss.clear(), ss.str("")
    #define INF 0x3f3f3f3f
    #define MOD 1000000007
    
    using namespace std;
    
    typedef long long ll;
    
    int main()
    {
        freopen("data.txt", "r", stdin);  // 必须创建,文件保存在源文件同个目录下
        freopen("data.txt", "w", stdout); // 无需创建自动生成
        /* 中间按原样写代码,什么都不用修改 */
    
        int n;
        while(~scanf("%d",&n))
        {
            printf("%d\n",n);
        }
    
        fclose(stdin);
        fclose(stdout);
    
        return 0;
    }

  4. URDL(二维 / 三维):
    // URDL:二维
    const int di[4]={-1,0,1,0};
    const int dj[4]={0,1,0,-1};
    
    // UDRL i from 0 to 3(二维)
    int dir[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };
    
    
    // URDL:三维
    const int X[6]={1,0,0,-1,0,0};
    const int Y[6]={0,1,0,0,-1,0};
    const int Z[6]={0,0,1,0,0,-1};
  5. 斐波那契数列与三角形的关系。点击打开链接
  6. 卡特兰数。点击打开链接
  7. 有时候,全局变量对 DFS 是禁忌,特别是每次回溯时需要变回起初的值的变量,如果用全局替代局部变量的话,一旦改变就回不去起初的“还原点”了。
  8. DFS 时,有时候可以用比较好看出的值代入看看是否正常,大概感觉对即可,不求甚解。
  9. 正整数分解成(可相同或不相同的数)使得乘积最大。点击打开链接
  10. 快速求解二进制 1 的个数。
    ll numOf1(ll n)
    {
        ll cnt = 0;
        while(n){
            cnt++;  // 只要 n 不为 0,则其至少有一个 1
            n = n & (n - 1);
        }
        return cnt;
    }
  11. 算法竞赛中,关闭iostream对象和cstdio流同步以提高输入输出的效率。
    1、即调用ios::sync_with_studio(false); 特别注意:关闭后C++ IO与C IO不能混用,cin不能与scanf,sscanf, getchar, fgets等混用,cout不能与printf,puts等混用,否则IO会混乱。
    2、在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。即调用std::cin.tie(0)。
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
  12. 多重循环直接跳出技巧:
    for(int i=0,f=1;i<n&&f;i++)
        for(int j=0;j<n&&f;j++)
            if(1) f=0;

  13. C++不检查数组越界情况?
    答:看编译器是否对这方面进行调改过,如果越界也不报运行错误的话,就报WA,但有些OJ编译器可以检查出的话就报RE。

  14. “printf、scanf” 和 其他表达式可以用 “,” 写成一句,但是 “break、continue、return” 必须独自一句。

  15. 一道题目多种解决方法时,比较下时间复杂度。比如:点击打开链接(m都一样的情况:以 n 为主体AC,但以 k 为主体TLE)

  16. 当人脑debug时,或者一些数据不确定时,选择数据量尽可能少的例子来确定代码,因为代码是通用的。

  17. 输出的时候,尽可能用“&”引用(参数复制速度比较慢,所以上引用)。

  18. PAT 中,gets 函数用不了。

  19. 对于一些分类的题目,可以用优先级标记来区分,这样只要一次sort即可,否则要多次sort,这样时间消耗方面还是有差别的。

  20. 有时候建树时,没出现的结点就是 root:

    for(int i=0;i<n;i++) 
        if(!vis[i]){rt=i; break;}
    
    /*
    9
    7 8
    - -
    - -
    - -
    0 1
    2 3
    4 5
    - -
    - -
    */

  21. 保留小数部分技巧:如果判断保留 n 位小数,则判断第 n+1 位数字与 “5” 做比较再进行处理。

  22. 完全二叉树判定:第一次出现空儿子后,接下来如果有出现真儿子,则 NO,否则 YES。

  23. 有些题目不要全都算出来,需要用到哪个就算哪个,这样可以节省没必要算的时间。

  24. 写在一个括号里的变量需要注意:如果 bfs 函数里有对 rs 操作则最终效果是不理想的,因为这里的 rs 在没运行 bfs 之前就已经旧值赋值上去了,即使中途改变了 rs,也是 rs 被更新,但是这里的 %d 输出仍然是之前未改变的 rs 值:

    printf(bfs()?"YES %d\n":"NO %d\n",rs);

  25. 小技巧:
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        if(i) join(a[i-1],a[i]);
    }

  26. 自定义稳定排序:添加个唯一值标号(从小到大比较)。
  27. char* 效率高于 string。
  28. 小技巧:写入名字 并 计算名字前有多少空格。
    int calSp(char name[])
    {
        char c;
        int cnt=0,k=0;
        while((c=getchar())==' ') cnt++;
    
        do
        {
            name[k++]=c;
        }while((c=getchar())!='\n');
        name[k]='\0';
    
        return cnt;
    }

  29. 50% 占比,小技巧:
    // 行数是列数的50%(四舍五入取整)
    int n; cin>>n;
    int col=n,row=n-n/2;

  30. 模仿 split 分割小技巧:
    string s="abc 123 . ",ts;
    stringstream ss(s);
    int k=0;
    while(ss>>ts)
    {
        k++;
        cout<<ts<<endl;
        cout<<k<<endl;
    }

  31. map<string, int> 搭配 scanf:
    map<string,int> mp;
    char s1[100];
    scanf("%s",s1);
    mp[s1]=1;
    
    // 容易 TLE
    string s2;
    cin>>s2;
    mp[s2]=1;

  32. 翻译:
    1、“a by b” // a * b

  33. 对于局部数组来讲:如果数组在定义声明顺便初始化的时候,只要前面有数据赋值,多少个都可以,至少有一个即可,则,其后的数组里的元素全部为0,所以一般 int a[100]={0}; 并不是把全部数组元素变为0,而是因为刚刚说的这个原理。如果改成 int a[100]={1,2,3}; 则前3个为1,2,3,但后面都为0。
  34. 首尾空格处理 + strncpy:
    // 首尾空格处理
    int l=0,r=k-1;
    while(s[l]==' ') l++;
    while(s[r]==' ') r--;
    
    strncpy(s,s+l,r-l+1);
    ts[r-l+1]=0;

  35. strtok 函数:
    char str[] = "I am a  stduent,you are teacher!";
    const char spliter[] = " ,!";
    
    char * pch;
    pch = strtok( str, spliter );
    while( pch != NULL )
    {
        cout << pch << endl;
        pch = strtok( NULL, spliter );
    }

  36. lower_bound / upper_bound 函数:
    /* 数组从 1 开始存储,但是查找的时候依然是从 0 开始,因为若当待查找的数据>最大值,return 数组大小;若<最小值,return 0;而如果不从 1 开始存储的话,当 return 0 时,不知道是查找到第一个数据还是因为没找到的缘故。 */
    int l=lower_bound(a,a+n+1,b)-a; // 在 a[i] 中,返回第一个大于等于 b 的数字的对应位置
    int r=upper_bound(a,a+n+1,b)-a; // 在 a[i] 中,返回第一个大于 b 的数字的对应位置
    int rs=r-l;                     // 表示有 rs 个等于 b 的数

  37. 四舍五入到整数小技巧:
    printf("%d",(int)(x+0.5)); // 比下面这种写法有时更加精确
    
    printf("%.0f",double(x));

  38. 上下界针对 y轴 来说,所以上界显然大于等于下界,也就是说:区间[下界,上界]。
  39. 进制互转:
    /* 10进制转任意进制 */
    void D2Any(int x,int r)
    {
        int a,b=x,k=0,ans[100];
    
        while(1)
        {
            a=b%r;
            b=b/r;
            ans[k++]=a;
            if(x/r==0) break;
            x/=r;
        }
    
        for(k--;k>=0;k--)
        {
            if(ans[k]>9)
                printf("%c",ans[k]+87); // 87:小写,55:大写
            else
                printf("%d",ans[k]);
        }
    }
    
    /* 任意进制转10进制 */
    ll Any2D(char x[],int r)
    {
        int len=strlen(x),tmp=1;
        ll ans=0;
        for(int i=len-1;i>=0;i--)
        {
            if(x[i]>='0' && x[i]<='9')
                ans+=(x[i]-'0')*tmp;
            else
                ans+=(x[i]-'a'+10)*tmp;
            tmp*=r;
        }
    
        return ans;
    }

  40. 0.000005 处理精度丢失问题,具体要加多少个“0”,看题目的保留多少位的情况再做分析。
  41. 变量的 初始化 和 定义,尽量跟业务逻辑的代码紧挨在一起;先业务逻辑,需要用到的变量再往上面补充。
  42. 日期比较 y*10000 + m*100 + d 小技巧。
  43. 差分前缀和小技巧:点击打开链接
  44. 输入一个十进制,统计它的二进制数中“1”的个数:
    int cnt1(int x)
    {
        int cnt=0;
        while(x) cnt++, x=x&(x-1);
    
        return cnt;
    }

  45. 输出格式控制:
    // 第一种 4 line 每次都要判断相对来说也消耗
    for(int i=0;i<n;i++)
        if(i) printf(" %d",a[i]);
        else printf("%d",a[i]);
    puts("");
    
    
    // 第二种 4 line
    printf("%d",a[0]);
    for(int i=1;i<n;i++)
        printf(" %d",a[i]);
    puts("");
    
    
    // 第三种 3 line
    // 最好不要从0开始,否则每次都要 -1 相对来说也要消耗(即使可忽略不计),所以从1开始比较好
    for(int i=1;i<n;i++)
        printf("%d ",a[i]);
    printf("%d\n",a[n]);
    
    
    // 第四种 2 line
    for(int i=0;i<n;i++)
        printf("%d%c",i==n-1?'\n':' ');

  46. 输入格式控制:
    char ch;
    scanf(" %c", &ch); // 百分号之前有一个空格,这样scanf会首先过滤掉所有的空格、制表符和换行符
    
    
    /* 常见吸收回车写法 */
    char ch;
    int n; 
    scanf("%d",&n);
    getchar();        // 不推荐
    scanf("%c",&ch);
    
    scanf("%d",&n);
    scanf(" %c",&ch); // 推荐

  47. 输入(一行有空格的字符串),希望不包括空格以 '\n' 结束:
    /* 用 do-while 免得最后一次数据('\n'前)还要额外处理 */
    do
    {
        scanf("%s",key);
        nds[i].kv.push_back(key);
    }while(getchar()!='\n');

  48. 利用格式符 “%[]” 作用:为扫描字符集合:
    scanf("%[^c]", str); 
    其中 “c” 是一个具体的字符常量(包括控制字符)。
    C语言中 scanf() 函数提供的 “%[]” 格式串可以用来进行多个字符的输入,并对结束符进行自定义。对于 %[] 还可以用 ^+ 任意字符(包括 eof)来结束字符串的输入,如 %[^EOF] 就是直到有 EOF 输入,字符串才中止。
    scanf("%[^\n]", str);  // 以换行符作为字符串输入的结束(可包含空格)

  49. 针对非升序 / 非降序操作,如果相等按照原来输入的顺序进行排序(通过伪优先级控制):
    struct node
    {
        int ge,pri;
    }nds[100005];
    
    int cmp(node n1,node n2) // 非升序
    {
        if(n1.ge==n2.ge) return n1.pri<n2.pri; 
        return n1.ge>n2.ge;
    }

  50. 自定义进制(模仿 hh: mm: ss):
    typedef long long ll;
    
    int main()
    {
        ll a1,b1,c1,a2,b2,c2;
        scanf("%lld.%lld.%lld%lld.%lld.%lld",&a1,&b1,&c1,&a2,&b2,&c2);
        ll rs=c1+b1*29+a1*17*29+c2+b2*29+a2*17*29;
        // rs(秒)    hh=rs/3600    mm=rs%3600/60    ss=rs%60
        printf("%lld.%lld.%lld\n",rs/(17*29),rs%(17*29)/29,rs%29);
    
        return 0;
    }

  51. 大数比较小技巧:点击打开链接
  52. double 判 0 小技巧:
    double A; scanf("%lf",&A);
    if(A+0.005>=0&&A<0) printf("0.00"); // 1
    if(fabs(A)<0.0005) printf("0.00");  // 2 推荐

  53. 矩阵区分八个方向是【越界】or【题目条件】的区分小技巧:
    int pas=1, k=0; // 这里 pas 不能从 0 开始,否则会影响判断
    
    if(i-1>=0 && pas++ && abs(a[i-1][j]-d)>st) k++;
    if(i-1>=0 && j+1<m && pas++ && abs(a[i-1][j+1]-d)>st) k++;
    if(i-1>=0 && j-1>=0 && pas++ && abs(a[i-1][j-1]-d)>st) k++;
    if(j-1>=0 && pas++ && abs(a[i][j-1]-d)>st) k++;
    if(j+1<m && pas++ && abs(a[i][j+1]-d)>st) k++;
    if(i+1<n && pas++ && abs(a[i+1][j]-d)>st) k++;
    if(i+1<n && j-1>=0 && pas++ && abs(a[i+1][j-1]-d)>st) k++;
    if(i+1<n && j+1<m && pas++ && abs(a[i+1][j+1]-d)>st) k++;
    
    if(k+1==pas) return 1;

  54. 有时候创建数据结构的情况比较复杂的时候,先不要急于创建,可以在思路Coding的过程中,发现需要用到哪个变量就创建哪个变量。
  55. 判断语句原则:先判断再决定;而不是先决定再判断。
  56. 数组初始化的三种常用方法:for循环浪费的时间最多,{0} 与 memset 耗时差不多。
  57. PAT的题目因为时间控制严格,而且它评测机制每次都一个测试用例一次,所以不到万不得已,不要像平常ACM一样去做清空操作。
  58. heap:
    // 边插入边建堆
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        make_heap(a+1,a+1+i,greater<int>()); // min heap
    }
    
    // 先建树再调整堆
    make_heap(a,a+n); // max heap

  59. 区间合并小技巧:点击打开链接
  60. 在DFS中途就可以输出结果并且结束整个程序技巧
    exit(0);

  61. '\0' 输出不是什么都没有,而是一个类似的空格,当它用 %c 单独输出时。
  62. 待更新...
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放羊的牧码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值