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("\n");
        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
  • 递推

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;
}

猜你喜欢

转载自www.cnblogs.com/scl0725/p/12315111.html