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

Chapter1 递归与递推的更多相关文章

  1. 斐波那契数列 递归 尾递归 递推 C++实现

    ==================================声明================================== 本文原创,转载请注明作者和出处,并保证文章的完整性(包括本 ...

  2. 动态规划——数字三角形(递归or递推or记忆化搜索)

    动态规划的核心就是状态和状态转移方程. 对于该题,需要用抽象的方法思考,把当前的位置(i,j)看成一个状态,然后定义状态的指标函数d(i,j)为从格子出发时能得到的最大和(包括格子本身的值). 在这个 ...

  3. 剑指offer-矩形覆盖-斐波那契数列(递归,递推)

    class Solution { public: int rectCover(int number) { if(number==0 || number==1||number==2) return nu ...

  4. P1044 栈(递归、递推、卡特兰、打表)

    P1044 栈 题目背景 栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表. 栈有两种最重要的操作,即pop(从栈顶弹出一个元素)和push(将一个元素进栈). 栈的重要 ...

  5. NOI / 2.3基本算法之递归变递推-6262:流感传染

    OpenJudge - 6262:流感传染http://noi.openjudge.cn/ch0203/6262/ 6262:流感传染​​​​​​ 总时间限制: 1000ms 内存限制: 65536k ...

  6. 从一道NOI练习题说递推和递归

    一.递推: 所谓递推,简单理解就是推导数列的通项公式.先举一个简单的例子(另一个NOI练习题,但不是这次要解的问题): 楼梯有n(100 > n > 0)阶台阶,上楼时可以一步上1阶,也可 ...

  7. 从三数之和看如何优化算法,递推-->递推加二分查找-->递推加滑尺

    人类发明了轮子,提高了力的使用效率. 人类发明了自动化机械,将自己从重复的工作中解脱出来. 提高效率的方法好像总是离不开两点:拒绝无效劳动,拒绝重复劳动.人类如此,计算机亦如是. 前面我们说过了四数之 ...

  8. 【uva 12627】Erratic Expansion(算法效率--递推)

    题意:初始1个红气球,每小时后,1个红气球会变成3个红气球和1个蓝气球,而1个蓝气球会变成4个蓝气球.问经过N小时后,第L~R行一共有多少个红气球. 解法:问行数就定义f[i][j]表示 i 小时后前 ...

  9. 百练2755 奇妙的口袋 【深搜】or【动规】or【普通递归】or【递推】

    总Time Limit:  10000ms  Memory Limit:  65536kB 有一个奇妙的口袋.总的容积是40,用这个口袋能够变出一些物品,这些物品的整体积必须是40.John如今有n个 ...

随机推荐

  1. 死磕java(4)

    数组 public void int4() {  int[] int2 = {1,2,3,4};  System.out.print(int2[2]); } 输出:3 另一种数组的初始化 public ...

  2. LUA提取免费迅雷账号

    --获取http://www.521xunlei.com/ 免费迅雷账号 function getPageid() local http = require("socket.http&quo ...

  3. 【限时免费】近1000G JAVA学习视频下载

    2020的情人节是个极特殊的情人节,面对肆虐的疫情,我们无法出门,宅在家里,也无法阻止你作为一名优秀程序员的梦想. 或许没有鲜花.没有蛋糕…… 姜小白就为大家备好了一份大礼,将自己近几年整理收藏的全网 ...

  4. cookie理解与实践【实现简单登录以及自动登录功能】

    cookie理解 Cookie是由W3C组织提出,最早由netscape社区发展的一种机制 http是无状态协议.当某次连接中数据提交完,连接会关闭,再次访问时,浏览器与服务器需要重新建立新的连接: ...

  5. Go语言标准库之net/http

    Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现. net/http介绍 Go语言内置的net/http包提供了HTTP客户端和服务端的实现. HTTP协议 超文本传输协 ...

  6. LeetCode 218. The Skyline Problem 天际线问题(C++/Java)

    题目: A city's skyline is the outer contour of the silhouette formed by all the buildings in that city ...

  7. Codeforces 1011C Fly(二分+模拟)

    题意: 是有n个星球,1代表地球,然后输入一个m表示火箭的重量,然后输入了两组n个数,第一组表示在每个星球起飞时消耗一吨燃料的质量数,a[i]就表示每a[i]吨就要消耗1吨燃料,第二组就表示在每个星球 ...

  8. Vsftpd: 基于PAM认证的虚拟用户和匿名用户

    目录 环境说明效果说明及截图①. 安装组件②. 系统账户建立③. 编辑vsftpd的配置文件④. 生成虚拟用户的数据库文件⑤. 生成一个使用vsftpd_login.db数据文件的PAM认证文件⑥. ...

  9. SqlServer触发器的基础知识

    触发器的基础知识:create trigger tr_name on table/view{for | after | instead of } [update][,][insert][,][dele ...

  10. 来去学习之---KMP算法--next计算过程

    一.概述 KMP算法是一种字符串匹配算法,比如现有字符串 T:ABCDABCDABCDCABCDABCDE, P:ABCDABCDE P字符串对应的next值:[0,0,0,0,1,2,3,4,0] ...