1.斐波那契数列
思路1:直接递归调用,时间复杂度
O(2N)
int Fibonacci(int n)
{
if(n==1 || n==2)
return n;
return Fibonacci(n-1) + Fibonacci(n-2);
}
思路2:动态规划,将前n次的结果保存,时间复杂度 O(N)
int Fibonacci(int n)
{
if(n==1 || n==2)
return n;
int now=1;
int pre=1;
for(int i=2;i<n;i++)
{
now=now+pre;
pre=now-pre;
}
return now;
}
思路3:
F(n)=F(n−1)+F(n−2)
可以写成矩阵的形式:
(F(n),F(n−1))=(F(n−1),F(n−2))∗[1110]
由上式可以得到:
(F(n),F(n−1))=(F(2),F(1))∗[1110]n−2
//注:懒得换编译器,还是用的vs2008,一些C++11的特性不支持,矩阵赋值比较繁琐,可以考虑重写个矩阵的类,重载=
vector<vector<int>> multMatrix(vector<vector<int>> a,vector<vector<int>> b)//矩阵乘法
{
int temp=0;
vector<int> tmp;
vector<vector<int>> res;
for(int i=0;i<a.size();i++)//m
{
for(int j=0;j<b[0].size();j++)//n
{
for(int k=0;k<a[0].size();k++)//n
{
temp+=a[i][k]*b[k][j];
}
tmp.push_back(temp);
temp=0;
}
res.push_back(tmp);
tmp.clear();
}
return res;
}
vector<vector<int>> powerN(vector<vector<int>> a,int n)//矩阵的n次方,这里直接写了2*2的单位阵
{
vector<vector<int>> tmp;
vector<vector<int>> sum;
vector<int> temp;
temp.push_back(a[0][0]);
temp.push_back(a[0][1]);
tmp.push_back(temp);
temp.clear();
temp.push_back(a[1][0]);
temp.push_back(a[1][1]);
tmp.push_back(temp);
temp.clear();
temp.push_back(1);
temp.push_back(0);
sum.push_back(temp);
temp.clear();
temp.push_back(0);
temp.push_back(1);
sum.push_back(temp);
int m=1;
while(n!=0)
{
if((n&1)!=0)
{
sum=multMatrix(sum,tmp);
}
tmp=multMatrix(tmp,tmp);
n=n>>1;
}
return sum;
}
int Fibonacci(int n)
{
if(n==1 || n==2)
return 1;
vector<vector<int>> a;
vector<vector<int>> b;
vector<int> temp;
temp.push_back(1);
temp.push_back(1);
a.push_back(temp);
temp.clear();
temp.push_back(1);
temp.push_back(0);
a.push_back(temp);
b= powerN(a,n-2);
return b[0][0]+b[1][0];
}
类似的问题还有:台阶问题(初始值不同),牛的繁殖问题 F(n)=F(n−1)+F(n−3)
2.矩阵的最小路径和:给定一个矩阵,从矩阵的左上角开始,每次向下或向右,最后到达右下角的位置,路径上的所有数字加起来就是路径和。
思路:dp[i][j]存储从左上角到达m[i][j]的最小路径,第一行和第一列的数即为该列/该列到该位置的和。从m[0][0]到m[i][j]的路径必经过m[i][j-1]或m[i-1][j],d[i][j]=min{d[i-1][j],d[i][j-1]}+m[i][j]
int minPath(vector<vector<int>> m)
{
vector<vector<int>> dp;
dp.resize(m.size());
for(int i=0;i<m.size();i++)
dp[i].resize(m[0].size());
dp[0][0]=m[0][0];
for(int j=1;j<m[0].size();j++)
dp[0][j]=dp[0][j-1]+m[0][j];
for(int i=1;i<m.size();i++)
dp[i][0]=dp[i-1][0]+m[i][0];
for(int i=1;i<m.size();i++)
for(int j=1;j<m.size();j++)
{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+m[i][j];
}
return dp[m.size()-1][m[0].size()-1];
}
思路2:减少空间复杂度的一种算法,如果列数大于行数,则从左往右更新数组,如果行数大于列数,则从上往下更新
int minPath2(vector<vector<int>> m)
{
vector<int> dp;
int more=max(m.size(),m[0].size());
int less=min(m.size(),m[0].size());
bool rowmore=more==m.size();//如果rowmore==1 说明行数大于列数
dp.resize(less);
dp[0]=m[0][0];
for(int i=1;i<less;i++)
dp[i]=dp[i-1]+(rowmore?m[0][i]:m[i][0]);
for(int i=1;i<more;i++)
{
dp[0]=dp[0]+(rowmore?m[i][0]:m[0][i]);
for(int j=1;j<less;j++)
dp[j]=min(dp[j],dp[j-1])+(rowmore?m[i][j]:m[j][i]);
}
return dp[less-1];
}
3.换钱的最少货币:定义一个数组,表示货币的面值,每种货币不限数目,再给定一个值aim表示要换的钱,给出一个表示aim的最少货币数。
变:换钱的方法数
#include <iostream>
#include <vector>
using namespace std;
/**************************暴力递归**************/
int process1(vector<int> arr,int index,int aim)
{
int res=0;
if (index==arr.size())
res=aim==0?1:0;
else
{
for(int i=0;arr[index]*i<=aim;i++)
res+=process1(arr,index+1,aim-arr[index]*i);
}
return res;
}
int coins1(vector<int> arr,int aim)
{
if(arr.size()==0 || aim==0)
return 0;
return process1(arr,0,aim);
}
/*********************************************************/
/****************************记忆搜索*********************/
//时间复杂度O(N*aim^2)
int process2(vector<int> arr,int index,int aim,vector<vector<int>> &map)
{
int res=0;
int mapvalue;
if (index==arr.size())
res=aim==0?1:0;
else
{
for(int i=0;arr[index]*i<=aim;i++)
{
mapvalue=map[index+1][aim-arr[index]*i];
if(mapvalue!=0)
{
res+=mapvalue==-1?0:mapvalue;
}
else
{
res+=process2(arr,index+1,aim-arr[index]*i,map);
}
}
}
map[index][aim]=res==0?-1:res;
return res;
}
int coins2(vector<int> arr,int aim)
{
vector<vector <int> > map(arr.size()+1 ,vector<int>(aim+1));
if(arr.size()==0 || aim==0)
return 0;
return process2(arr,0,aim,map);
}
/*********************************************************/
/****************************动态规划*********************/
//时间复杂度O(N*aim^2)
int coins3(vector<int> arr,int aim)
{
int n=arr.size();
vector<vector<int>> dp(n,vector<int>(aim+1));
for(int i=0;i<n;i++)
{
dp[i][0]=1;
}
for(int j=0;j<=aim;j++)
{
if(j%arr[0]==0)
{
dp[0][j]=1;
}
else
{
dp[0][j]=INT_MAX;
}
}
int res=0;
for(int i=1;i<n;i++)
for(int j=1;j<=aim;j++)
{
for(int k=0;j-arr[i]*k >=0;k++)
{
if(dp[i-1][j-arr[i]*k]!=INT_MAX)
res+=dp[i-1][j-arr[i]*k];
}
dp[i][j]=res;
res=0;
}
return dp[n-1][aim];
}
/*********************************************************/
/****************************动态规划2*********************/
//时间复杂度O(N*aim)
int coins4(vector<int> arr,int aim)
{
int n=arr.size();
vector<vector<int>> dp(n,vector<int>(aim+1));
for(int i=0;i<n;i++)
{
dp[i][0]=1;
}
for(int j=0;j<=aim;j++)
{
if(j%arr[0]==0)
{
dp[0][j]=1;
}
else
{
dp[0][j]=INT_MAX;
}
}
int res=0;
for(int i=1;i<n;i++)
for(int j=1;j<=aim;j++)
{
res=dp[i-1][j];
if(j-arr[i]>=0 && dp[i][j-arr[i]]!=INT_MAX)
if(res!=INT_MAX)
res+=dp[i][j-arr[i]];
else
res=dp[i][j-arr[i]];
dp[i][j]=res;
res=0;
}
return dp[n-1][aim];
}
int main(){
vector<int> arr;
arr.push_back(5);
arr.push_back(10);
arr.push_back(25);
arr.push_back(1);
int aim=15;
// int res1=coins1(arr,aim);
// cout<<res1<<endl;
// int res2=coins2(arr,aim);
// cout<<res2<<endl;
// int res3=coins3(arr,aim);
// cout<<res3<<endl;
int res4=coins4(arr,aim);
cout<<res4<<endl;
return 0;
}