给定一个数组,例如
[45,54,2,99,1]
给定一个长度l,比如100
能够对数组中相邻的两个数交换,前提必须满足两个数的和不大于l
[45,54,2,99,1]
给定一个长度l,比如100
能够对数组中相邻的两个数交换,前提必须满足两个数的和不大于l
求通过这样的交换所可以产生的全排列有多少种?对于同样的数,也默认它们不同。
这是某外企的电面题,感觉要找到比較好的算法还是挺难的。眼下想到的是一种循环暴力搜索加暴力剪枝并回溯的,从測试数据来看。效果还凑合。
思路是:
暴力搜索:查看不论什么一个数,看能否够交换到第一位。假设能够。则规模就能够减少而且递归,复杂度为O(n^3+n!)
int orderCount(int *a, int n, int l){ if(n <= 1)return 1; int ans = 0; for(int i = 0;i < n;i ++){ int j; for(j = i-1;j >= 0;j --) if(a[i]+a[j] > l) break; if(j >= 0)continue; int t = a[i]; for(j = i-1;j >= 0;j --) a[j+1] = a[j]; ans += orderCount(a+1,n-1,l); for(j = 0;j < i;j ++) a[j] = a[j+1]; a[i] = t; } return ans; }
剪枝:
1. 假设一个数能够和所以其它的都交换,这个数将相当于剩下的数的全排列然后这个数随意插入,即问题就变成n*f(n-1)
2. 假设一个数不能和不论什么其它的数交换,它将时问题变成左右的全排列相乘,及f(i)*f(n-i-1)
//some additional optimization //If one number could not exchange with any other, it would divide the problem into two subproblem //If one number could exchange with any other, n multiply the left exchange solutions int order(int *a, int n, int l){ int exn = 0; int *exp = new int[n]; for(int i = 0;i < n;i ++){ int ex = 0; int unex = 0; for(int j = 0;j < n;j ++){ if(i == j)continue; if(a[i] + a[j] <= l) ex ++; else unex ++; } if(unex == n-1) return orderCount(a,i,l)*orderCount(a+i+1,n-i-1,l); if(ex == n-1){ int t = a[i]; for(int j = i-1;j >= exn;j --) a[j+1] = a[j]; exp[exn] = i; a[exn ++] = t; } } int x = 1; for(int i = 0;i < exn;i ++) x *= n-i; return x*orderCount(a+exn,n-exn,l); }
回溯:
上面的剪枝,我们仅仅是对最開始的数据进行了剪枝,后来的搜索就不再处理剪枝了,所以效率并不高。那我们就要考虑是否可以每个递归都来做剪枝。而这里由于数组的位置变化会影响终于的结果。所以无论是搜索剪枝的过程,完毕之后一定要恢复原来的数组。这样才不会影响其它接下来过程的结果。于是就得到终于的每一步都剪枝的循环方法。
经过測试,这样的方法对于測试数据可以高速得到结果,明显优于上面的单纯暴力方法。
int order(int *a, int n, int l); //The code modified in the interview int orderCount(int *a, int n, int l){ if(n <= 1)return 1; int ans = 0; for(int i = 0;i < n;i ++){ int j; for(j = i-1;j >= 0;j --) if(a[i]+a[j] > l) break; if(j >= 0)continue; int t = a[i]; for(j = i-1;j >= 0;j --) a[j+1] = a[j]; //ans += orderCount(a+1,n-1,l); ans += order(a+1,n-1,l); for(j = 0;j < i;j ++) a[j] = a[j+1]; a[i] = t; } return ans; } //some additional optimization //If one number could not exchange with any other, it would divide the problem into two subproblem //If one number could exchange with any other, n multiply the left exchange solutions int order(int *a, int n, int l){ int exn = 0; int *exp = new int[n]; for(int i = 0;i < n;i ++){ int ex = 0; int unex = 0; for(int j = 0;j < n;j ++){ if(i == j)continue; if(a[i] + a[j] <= l) ex ++; else unex ++; } if(unex == n-1) return orderCount(a,i,l)*orderCount(a+i+1,n-i-1,l); if(ex == n-1){ int t = a[i]; for(int j = i-1;j >= exn;j --) a[j+1] = a[j]; exp[exn] = i; a[exn ++] = t; } } int x = 1; for(int i = 0;i < exn;i ++) x *= n-i; int ans = x*orderCount(a+exn,n-exn,l); while(exn){ exn --; int t = a[exn]; for(int j = exn;j < exp[exn];j ++) a[j] = a[j+1]; a[exp[exn]] = t; } return ans; } int _tmain(int argc, _TCHAR* argv[]) { int b[] = {45,54,2,80,3,69,2,65,94,74,80,23,8,5,9,3,18,67,99}; clock_t t1,t2,t3; t1 = clock(); cout<<orderCount(b,19,100)<<endl; t2 = clock(); cout<<order(b,19,100)<<endl; t3 = clock(); cout<<t2-t1<<" vs "<<t3-t2<<endl; char ch; cin>>ch; return 0; }
版权声明:本文博客原创文章,博客,未经同意,不得转载。