首先直接暴力的时间复杂度很大,下面先贴一个暴力法的结果,然后再是动态规划
class Solution {
public:
int num=0;
void dfs(int &sum,int &cur,vector<int> &re,vector<int> &der)
{
// if(cur>=re.size()) return;
if(sum==re.size()-1&&cur>=re.size()) {//说明一次排列完成
num++;
return;
}
for(int i=cur;i<re.size();++i)
{
// if(der[i]==0)//i位置未放置数字,则开始放置
//{//cur位置还没放置数字
for(int j=1;j<re.size();++j)//找到一个可以放置的数字开始放置
{
if(re[j]==0&&j!=i)//找到一个符合规则的数字
{
re[j]=1; der[i]=1;// result[i]=j;
sum++;cur++;
dfs(sum,cur,re,der);
re[j]=0;
der[i]=0; //回溯
//result[i]=0;
sum--;cur--;
break; //找到一条路径
}
}
// }
}
}
int findDerangement(int n) {
//经典全排列,考虑使用dfs
vector<int> re(n+1,0);//用来标记该下标的数字是否放置过
vector<int> der(n+1,0);//用来标记是否已经放置了数字
//vector<int> result(n+1,0);//用来标记是否已经放置了数字
int a=0,b=1;
dfs(a,b,re,der);
return num;
}
};
n=10 时:
n=11:
n=12: 暴力法的耗时炸了,十倍增长,恐怖如斯
下面就要有请万能的动态规划了:
一共有n个数,假设第k个数被放置在非k的x位置,那么就需要对剩下的数接着进行错位排列:
一种直接k与x互换,那么相当于剩下n-2个数的错位排序
一种x不能放在k位置上,其他非k非x的数i,不能放在i位置上,这样从整体上看就还是n-1个数的错位排列
方法有的时候比努力重要的多啊(指好的算法比暴力好多了)
那么记f(n)为n个数错位排列的种类,则有
f(n)=(f(n-1)+f(n-2)) *(n-1)
代码: 时间复杂度O(n),空间复杂度O(1)
class Solution {
public:
int findDerangement(int n) {
long long f1=0,f2=1,fn=1;
if(n<=1) return 0;
if(n==2) return 1;
for(int i=3;i<=n;i++)
{
fn=(f1+f2)*(i-1)%(1000000007);
f1=f2;
f2=fn;
}
return fn;
}
};