大家好这里是TKLA…初学算法的大一同学~现在已经系统学习过了C语言,正在学习C++以及python…希望能在CSDN上收获到很多知识!也希望自己能成为分享知识的一员!
目前学习的教材是这本 《算法竞赛进阶指南》(李煜东 著)
这本书按照 0x
加上两个16进制数字组成,还是比较有趣的
大概就是将题目意思直接用算式表达出来的方法
原书关于这部分的阐述…感觉比较基础…这里直接用两个例子来演示两者
递推
typedef long long ll;
ll fic(int n){
ll ans = 1;
for(int i=2;i<=n;++i)
ans *= i;
return ans;}
递归
typedef long long ll;
ll fic(int n){
return n?n*fic(n-1):1;}
递推
typedef long long ll;
ll fib(int n){
ll a = 1,b = 1,c;
for(int i=1;i<n;++i){
c = a + b;
a = b;
b = c;}
return a;}
递归
typedef long long ll;
ll fib(int n){
return (n<2)?1:fib(n-1)+fib(n-2);}
对比可以观察到以下几点:
区别 | 递推 | 递归 |
---|---|---|
结构 | 循环结构 | 选择结构 |
代码量 | 多 | 少 |
然而事实上,复杂度与代码量的多少没有关系,更重要的是递归/递推方法(算法、实际问题),书中给到了几个应用模式。
枚举形式 | 规模 | 一般遍历方式 |
---|---|---|
多项式 | nk | 循环、递推 |
指数 | kn | 递归、位运算 |
排列 | n ! | 递归、next_permutation |
组合 | Cnm | 递归+剪枝 |
指数量枚举
#include
#include
#include
using namespace std;
int n;
vector<int> chosen;
void calc(int x){
if(x == n+1){
for(unsigned int i=0;i<chosen.size();++i)
printf("%d ",chosen[i]);
puts("");
return;}
calc(x+1);
chosen.push_back(x);
calc(x+1);
chosen.pop_back();}
int main(void){
cin >> n;
calc(1);
return 0;}
组合量枚举
只多了一句而已
#include
#include
#include
using namespace std;
int n,m;
vector<int> chosen;
void calc(int x){
if(chosen.size() > m||chosen.size()+(n-x+1)<m)
return;
if(x == n+1){
for(unsigned int i=0;i<chosen.size();++i)
printf("%d ",chosen[i]);
puts("");
return;}
chosen.push_back(x);
calc(x+1);//注意顺序
chosen.pop_back();
calc(x+1);}
int main(void){
cin >> n >> m;
calc(1);
return 0;}
排列量枚举
#include
#include //当然是使用全排列啦~
using namespace std;
const int MAX_N=11;
int n,a[MAX_N];
int main(void){
scanf("%d",&n);
for(int i=1;i<=n;i++)
a[i]=i;
do{
for(int i=1;i<=n;++i)
cout << a[i] << " ";
cout << endl;
}while(next_permutation(a+1,a+n+1));
return 0;}
费解的开关
这道题和POJ熄灯问题题干是一样的,所以我直接写POJ这道题解答就可以了叭…
此题大致意思是说,按下一个开关同时会使得毗邻位点开关也响应,目标熄灭所有的灯。
1.每个开关最多按一次可使得最终方法数最小
2.开关顺序与结果无关
3.熄灭一行灯可以通过熄灭下一行的灯实现
Sample Input
2
0 1 1 0 1 0
1 0 0 1 1 1
0 0 1 0 0 1
1 0 0 1 0 1
0 1 1 1 0 0
0 0 1 0 1 0
1 0 1 0 1 1
0 0 1 0 1 1
1 0 1 1 0 0
0 1 0 1 0 0
Sample Output输出操作方案,1表示按此处的开关
PUZZLE #1
1 0 1 0 0 1
1 1 0 1 0 1
0 0 1 0 1 1
1 0 0 1 0 0
0 1 0 0 0 0
PUZZLE #2
1 0 0 1 1 1
1 1 0 0 0 0
0 0 0 1 0 0
1 1 0 1 0 1
1 0 1 1 0 1
emm…首先嗷,采取枚举即可
#include
using namespace std;
int p[6][8],press[6][8];
bool guess(){
int c,r;
for(r=1;r<5;++r)
for(c=1;c<7;++c)
press[r+1][c] = (p[r][c]+press[r][c]+press[r-1][c]+press[r][c-1]+press[r][c+1])%2;
for(c=1;c<7;++c)
if((press[5][c]+press[4][c]+press[5][c-1]+press[5][c+1])%2 != p[5][c])
return false;//能否全熄灭
return true;}
void enumrate(){
int c;
for(c=1;c<7;++c)
press[1][c] = 0;
while(!guess()){
++press[1][1];
c = 1;
while(press[1][c]>1){
press[1][c] = 0;
++c;
++press[1][c];/*进位*/}}
return;}
int main(void){
int cases;
scanf("%d",&cases);
for(int r=0;r<6;++r)
press[r][0] = press[r][7] = 0;
for(int c=1;c<7;++c)
press[0][c] = 0;
for(int i=0;i<cases;++i){
for(int r=1;r<6;++r)
for(int c=1;c<7;++c)
scanf("%d",&p[r][c]);
enumrate();
printf("PUZZLE #%d\n",i+1);//这里有空格的,不写就会PE
for(int r=1;r<6;++r){
for(int c=1;c<7;++c)
printf("%d ",press[r][c]);
printf("\n");}}
return 0;}
没用递归是不是跑题了
复习:基本汉诺塔
An = 2 An-1 + 1根据不动点原理,x = -1,两边减去x,得 An = 2n - 1
复习:汉诺双塔
洛谷此题
显然,An = 2n+1 - 2
POJ此题
An = { 1,i=1; min{2Ai-k+2k-1},else}
关于怎么理解这个式子…用一个图来解释下叭!