zoukankan      html  css  js  c++  java
  • Chapter1 递归与递推

    Chapter 1 递归与递推

    • 时间复杂度(转载自yxc大佬)

    一般ACM或者笔试题的时间限制是1秒或2秒。
    在这种情况下,C++代码中的操作次数控制在 107107 为最佳。

    下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:

    n≤30n≤30, 指数级别, dfs+剪枝,状态压缩dp
    n≤100n≤100 => O(n3)O(n3),floyd,dp
    n≤1000n≤1000 => O(n2)O(n2),O(n2logn)O(n2logn),dp,二分
    n≤10000n≤10000 => O(n∗n√)O(n∗n),块状链表
    n≤100000n≤100000 => O(nlogn)O(nlogn) => 各种sort,线段树、树状数组、set/map、heap、dijkstra+heap、spfa、求凸包、求半平面交、二分
    n≤1000000n≤1000000 => O(n)O(n), 以及常数较小的 O(nlogn)O(nlogn) 算法 => hash、双指针扫描、kmp、AC自动机,常数比较小的 O(nlogn)O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
    n≤10000000n≤10000000 => O(n)O(n),双指针扫描、kmp、AC自动机、线性筛素数
    n≤109n≤109 => O(n√)O(n),判断质数
    n≤1018n≤1018 => O(logn)O(logn),最大公约数

    cin cout 与 scanf printf 如何选择

    如果处理数据个数小于10 ^ 5,都可以

    如果处理数据个数大于等于10 ^ 5,建议用 scanf printf

    • 递归 dfs

    1.递归实现指数型枚举 92

    可以画递归搜索树帮助理解分析

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 15;
    int m;
    int a[maxn];//存放数字状态 0 未考虑, 1选 2不选
    
    void pit (int n){
        if(n == m){
            for(int i = 0; i < maxn; i++)
                if(a[i] == 1)   printf("%d ", i + 1);
            printf("
    ");
            return;
        }
        a[n] = 2;
        pit(n + 1);//第一个分支
        a[n] = 0;//恢复现场 (可有可无 帮助理解)
        a[n] = 1;
        pit(n + 1);//第二个分支
    }
    
    int main()
    {
       while(cin >> m){
       pit(0);
       }
       return 0;
    }
    
    2.递归实现排列型枚举 94
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    //按照字典序输出
    using namespace std;
    
    int m;
    const int maxn = 10;
    bool used[maxn];//检查每个数字是否被用过, false表示没被用过,true表示用过了
    int st[maxn];//0表示还没存放数字 1~m表示已经存放,存放的是谁
    
    void dfs(int n)
    {
        if(n > m)
            {
                for(int i = 1; i <= m; i++)
                    printf("%d ", st[i]);
                puts("");
                return;
            }
        for(int i = 1; i <= m; i++)
            if(!used[i]){
                st[n] = i;
                used[i] = true;
                dfs(n + 1);
                //恢复现场
                st[n] = 0;
                used[i] = false;
            }
    }
    
    
    int main()
    {
        scanf("%d", &m);
        dfs(1);
        return 0;
    }
    
    3.递归实现组合型枚举 93
    • Time limit exceeded
    //答案对,会超时!!(下面有第二种方法)
    
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 50;
    int n, m;
    int st[maxn];//0表示这一位没有数字占用,1~n表示有并且是谁占用
    bool used[maxn];//false表示没有用过,true表示已经用过
    
    void dfs(int i)
    {
        if(i > m)
        {    
            int flag = 1;
            for(int j = 1; j < m ; j++)
                if(st[j + 1] < st[j]){  flag = 0;   break;}
            if(flag)
            {  
                for(int j = 1; j <= m; j++)
                printf("%d ", st[j]);
                puts("");
            }
            return;
        }
        for(int j = 1; j <= n; j++)
            if(!used[j]){
                st[i] = j;
                used[j] = true;
                dfs(i + 1);
                //恢复现场
                st[i] = 0;
                used[j] = false;
            }
                
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        dfs(1);
        return 0;
    }
    
    • AC code
    #include <cstring>
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 30;
    int n, m;
    int st[maxn];
    
    void dfs(int u, int start)//start用来确保下一位必定比前一位要大
    {
        if(u + n - start < m)	return;//减支,提高程序运行效率
        if(u > m){//边界
            for(int i = 1; i <= m; i++)
                printf("%d ", st[i]);
            puts("");
            return;
        }
        for(int i = start; i <= n; i++)
        {
                st[u] = i;
                dfs(u + 1, i + 1);
                //恢复状态,可有可无 帮助理解
                st[u] = 0;
        }
    }
    
    
    int main()
    {
        scanf("%d%d", &n, &m);
        dfs(1, 1);
        return 0;
    }
    
    4.带分数 1209
    //y总答案
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 10;
    
    int n;
    bool st[N], backup[N];
    int ans;
    
    bool check(int a, int c)
    {
        LL b = n * (LL)c - a * c;
    
        if (!a || !b || !c) return false;
    
        memcpy(backup, st, sizeof st);
    
        while (b)
        {
            int x = b % 10;     // 取个位
            b /= 10;    // 个位删掉
            if (!x || backup[x]) return false;
            backup[x] = true;
        }
    
        for (int i = 1; i <= 9; i ++ )
            if (!backup[i])
                return false;
    
        return true;
    }
    
    void dfs_c(int u, int a, int c)
    {
        if (check(a, c)) ans ++ ;
    
        for (int i = 1; i <= 9; i ++ )
            if (!st[i])
            {
                st[i] = true;
                dfs_c(u + 1, a, c * 10 + i);
                st[i] = false;
            }
    }
    
    void dfs_a(int u, int a)
    {
        if (a >= n) return;
        if (a) dfs_c(u, a, 0);
    
        for (int i = 1; i <= 9; i ++ )
            if (!st[i])
            {
                st[i] = true;
                dfs_a(u + 1, a * 10 + i);
                st[i] = false;
            }
    }
    
    int main()
    {
        cin >> n;
    
        dfs_a(0, 0);
    
        cout << ans << endl;
    
        return 0;
    }
    
    • 递推

    1.简单的斐波那契
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n;
    int fb[1000000];
    
    int main()
    {
        cin >> n;
        fb[0] = 0;
        fb[1] = 1;
        for(int i = 2; i < n; i++)
            fb[i] = fb[i - 1] + fb[i - 2];
        for(int i = 0; i < n; i++)
            printf("%d ", fb[i]);
        return 0;
    }
    
    2.费解的开关 95
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int dx[5] = {0, 0, 1, 0, -1}, dy[5] = {0, 1, 0, -1, 0};//坐标偏移变量
    const int maxn = 6;
    char g[maxn][maxn], backup[maxn][maxn];//backup为备份数组
    
    void turn(int x, int y)
    {
        for(int i = 0; i < 5; i++)
        {
            int a = x + dx[i], b = y + dy[i];
            if(a < 0 || a >= 5 || b < 0 || b >=5)   continue;
            g[a][b] ^= 1;
        }
    }
    
    int main()
    {
        int n;
        cin >> n;
        while(n--)
        {
            for(int i = 0; i < 5; i++)  cin >> g[i];
            int step = 10;
            for(int i = 0; i < 32; i++)//第一行有32种情况,二进制0/1表示是否操作
            {
                int res = 0;
                memcpy(backup, g, sizeof g);
                for(int j = 0; j < 5; j++)
                    if(i >> j & 1)
                    {
                        res++;
                        turn(0, 4 - j);//没搞懂,为什么不是turn(0, 4 - j);
                    }
                for(int i = 0; i < 4; i++)
                    for(int j = 0; j < 5; j++)
                        if(g[i][j] == '0')
                        {
                            res++;
                            turn(i + 1, j);
                        }
                bool dark = false;
                for(int i = 0; i < 5; i++)
                    if(g[4][i] == '0')  {dark = true;   break;}
                if(!dark)   step = min(step, res);
                memcpy(g, backup, sizeof g);
            }
            if(step > 6) step = -1;//变量很巧妙,用了在for循环外层就定义的step用min来承接res
            cout << step << endl;
        }
        return 0;
    }
    
    3.飞行员兄弟 116

    step 1: 计算复杂度 2 ^ 16 * (16 * 7 + 16 + 16)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    
    using namespace std;
    
    typedef pair<int , int> PII;
    
    #define x first
    #define y second
    
    const int maxn = 5;
    char g[maxn][maxn], backup[maxn][maxn];
    
    int code(int i, int j)//因为要字典序输出坐标,所以为了方便把二维数组标号
    {
        return i * 4 + j;
    }
    
    void turn_one(int x, int y)
    {
        if(g[x][y] == '+')  g[x][y] = '-';
        else g[x][y] = '+';
    }
    
    void turn_all(int x, int y)
    {
        for(int i = 0; i < maxn - 1; i++)
        {
            turn_one(x, i);
            turn_one(i, y);
        }
        turn_one(x, y);//(x, y)turn了两次,需要再turn回来
    }
    
    int main()
    {
        for(int i = 0; i < maxn - 1; i++)   cin >> g[i];
        vector<PII> res;//承担结果的PII定义位置
        for(int op = 0; op < 1 << 16; op++)
        {
            vector<PII> temp;
            
            memcpy(backup, g, sizeof g);//枚举前的备份,因为要修改
            
            for(int i = 0; i < maxn - 1; i++)
                for(int j = 0; j < maxn - 1; j++)
                {
                    int res = 0;
                    if(op >> code(i, j) & 1)//巧妙的移位让输出的坐标已经是字典序排行
                    {
                        temp.push_back({i, j});
                        turn_all(i, j);
                        res++;
                    }
                }
                
            bool has_closed = false;//判断有没有灯是灭的
            for(int i = 0; i < maxn - 1; i++)
                for(int j = 0; j < maxn - 1; j++)
                    if(g[i][j] == '+')
                        has_closed = true;
            if(!has_closed)            
                if(res.empty() || res.size() > temp.size()) res = temp;
            memcpy(g, backup, sizeof g);
        }
        
        cout << res.size() << endl;
        for(auto zm : res)   cout << zm.x + 1 << " " << zm.y + 1 << endl;
        
        return 0;
    }
    
    4.翻硬币 1208
    //虽然说要输出最少次数,实验推演之后可以知道其实只有唯一的结果
    //想象每两个硬币中间有一个决定是否翻面的开关
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 105;
    char begin1[maxn], end1[maxn];
    int s, res;
    
    void turn(int u)
    {
        if(begin1[u] == 'o') {begin1[u] = '*';return;}
        if(begin1[u] == '*') {begin1[u] = 'o';return;}
    }
    
    int main()
    {
        cin >> begin1 >> end1;
        s = strlen(begin1);
        for(int i = 0; i < s - 1; i++)
            if(begin1[i] != end1[i])
            {
                turn(i);
                turn(i + 1);
                res++;
            }
        cout << res << endl;
        return 0;
    }
    
  • 相关阅读:
    为什么要设计好目录结构?
    python 程序退出方式
    mysql超出最大连接数解决方法
    服务器网络连接状态
    Python判断文件是否存在的三种方法【转】
    Nginx日志中的金矿 -- 好文收藏
    vsphere中的linux虚拟机安装vmware-tools
    vsphere中的vcenter创建esxi模板虚拟机新建无法连接网络
    linux同步系统时间
    Linux内核中TCP SACK机制远程DoS预警通告
  • 原文地址:https://www.cnblogs.com/scl0725/p/12315111.html
Copyright © 2011-2022 走看看