请用分治算法求解一个包含 n 元素的数组中最大元素的位置 请用分治算法求解一个包含n元素的数组中最大元素的位置 请用分治算法求解一个包含n元素的数组中最大元素的位置
1)对所有元素分成两组,中间下标记为mid=(low+high)/2,使用自定义的findmax函数选出(low,mid)和(mid+1,high)中的最大值,而后比较选出二者间的最大值。
2)代码:
int search(vector<int>& arr) {
int low = 0;
int high = arr.size() - 1;
int loc = 0;
if(high==low)
{
return 0;
}
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] > arr[loc]) {
loc = mid;
}
if (arr[mid] < arr[mid + 1]) {
low = mid + 1;
}
else {
high = mid - 1;
}
}
return loc;
}
int main() {
int n;cin>>n;
vector<int>arr(n,0);
for(int i=0;i<n;++i)
{
cin>>arr[i];
}
int loc = search(arr);
cout << loc << endl;
return 0;
}
3)时间复杂度:
T(n)=2*T(n/2)+5; (n>1)
T(1)=O(1); (n=1)
对于n>1时,令a=2,b=2,f(n)=5= n l o g 2 ( 2 − 1 ) n^{log_{2}(2-1)} nlog2(2−1)
则T(n)=O(n).
2.设X, Y是两个n位的十进制数,求X*Y。请写出它的算法描述
算法描述:
输入:两个n位十进制数
输出:乘积
multi(x,y)
{
if(x==0||y==0) return 0;
if(n==1) return x*y;
n<- n/2;
x1<- x / (int)pow(10,n) ,x0<- x % (int)pow(10,n);
y1<- y/ (int)pow(10,n),y0<- y % (int)pow(10,n);
xy1<- multi(x1,y1);
xy0<- multi(x0,y0);
sum<- (x1-x0) * *(y0-y1);
return xy1* **pow(10,(2* * half) ) + (sum+xy1+xy0)*pow(10,half) +xy0;
}
核心操作:
代码:
long long multi(long long num1,long long num2)
{
if(num1<10 ||num2<10)return num1*num2;
int size1=ceil(log(num1)/log(10));
int size2=ceil(log(num2)/log(10));
int mid=max(size1,size2)/2;
long long x1=num1 / (int)pow(10,mid) ,x0= num1 % (int)pow(10,mid);
long long y1=num2/ (int)pow(10,mid),y0=num2 % (int)pow(10,mid);
long long xy1=multi(x1,y1);
long long xy0=multi(x0,y0);
long long sum= (x1-x0)*(y0-y1);
return xy1*pow(10,(2*mid) ) + (sum+xy1+xy0)*pow(10,mid) +xy0;
}
时间复杂度: T ( n ) = 3 ∗ T ( n / 2 ) + c ∗ n T(n)=3*T(n/2)+c*n T(n)=3∗T(n/2)+c∗n
设 a = 3 , b = 2 , f ( n ) = c ∗ n a=3,b=2 ,f(n)=c*n a=3,b=2,f(n)=c∗n, , f ( n ) = f(n)= f(n)=
则取 ε = l o g 2 3 / 2 > 0 ε=log_{2}3/2>0 ε=log23/2>0 得到
1)普通的二分法主要代码:
void bs(int n,Mat a,Mat b,Mat c)
{
if(n==1)
{
c.m[0][0]=ma.m[0][0] * mb.m[0][0];
return ;
}
else if(n==2)
{
c.m[0][0]=a.m[0][0]*b.m[0][0]+a.m[0][1]*b.m[1][0];
c.m[0][1]=a.m[0][0]*b.m[0][1]+a.m[0][1]*b.m[1][1];
c.m[1][0]=a.m[1][0]*b.m[0][0]+a.m[1][1]*b.m[1][0];
c.m[1][1]=a.m[1][0]*b.m[0][1]+a.m[1][1]*b.m[1][1];
return ;
}
Mat a00(n/2),a01(n/2),a10(n/2),a11(n/2);
Mat b00(n/2),b01(n/2),b10(n/2),b11(n/2);
for(int i=0;i<n/2;++i)
{
for(int j=0;j<n/2;++j)
{
a00.m[i][j]=a.m[i][j];
b00.m[i][j]=b.m[i][j];
a01.m[i][j]=a.m[i][j+n/2];
b01.m[i][j]=b.m[i][j+n/2];
a10.m[i][j]=a.m[i+n/2][j];
b10.m[i][j]=b.m[i+n/2][j];
a11.m[i][j]=a.m[i+n/2][j+n/2];
b11.m[i][j]=b.m[i+n/2][j+n/2];
}
}
Mat ab0000(n/2);
bs(n/2,a00,b00,ab0000);
Mat ab0110(n/2);
bs(n/2,a01,b10,ab0110);
Mat ab0001(n/2);
bs(n/2,a00,b01,ab0001);
Mat ab0111(n/2);
bs(n/2,a01,b11,ab0111);
Mat ab1000(n/2);
bs(n/2,a10,b00,ab1000);
Mat ab1110(n/2);
bs(n/2,a11,b10,ab1110);
Mat ab1001(n/2);
bs(n/2,a10,b01,ab1001);
Mat ab1111(n/2);
bs(n/2,a11,b11,ab1111);
Mat c00(n/2),c01(n/2),c10(n/2),c11(n/2);
c00=ab0000+ab0110;
c01=ab0001+ab0111;
c10=ab1000+ab1110;
c11=ab1001+ab1111;
for(int i=0;i<n/2;++i)
{
for(int j=0;j<n/2;++j)
{
c.m[i][j]=c00.m[i][j];
c.m[i][j+n/2]=c01.m[i][j];
c.m[i+n/2][j]=c10.m[i][j];
c.m[i+n/2][j+n/2]=c11.m[i][j];
}
}
}
//V S
void vs(int n,Mat a,Mat b,Mat c)
{
if(n==1)
{
c.m[0][0]=ma.m[0][0] * mb.m[0][0];
return ;
}
else if(n==2)
{
c.m[0][0]=a.m[0][0]*b.m[0][0]+a.m[0][1]*b.m[1][0];
c.m[0][1]=a.m[0][0]*b.m[0][1]+a.m[0][1]*b.m[1][1];
c.m[1][0]=a.m[1][0]*b.m[0][0]+a.m[1][1]*b.m[1][0];
c.m[1][1]=a.m[1][0]*b.m[0][1]+a.m[1][1]*b.m[1][1];
return ;
}
Mat a00(n/2),a01(n/2),a10(n/2),a11(n/2);
Mat b00(n/2),b01(n/2),b10(n/2),b11(n/2);
for(int i=0;i<n/2;++i)
{
for(int j=0;j<n/2;++j)
{
a00.m[i][j]=a.m[i][j];
b00.m[i][j]=b.m[i][j];
a01.m[i][j]=a.m[i][j+n/2];
b01.m[i][j]=b.m[i][j+n/2];
a10.m[i][j]=a.m[i+n/2][j];
b10.m[i][j]=b.m[i+n/2][j];
a11.m[i][j]=a.m[i+n/2][j+n/2];
b11.m[i][j]=b.m[i+n/2][j+n/2];
}
}
int half=n/2;
Mat m1(n/2);
vs(n/2,a00+a11,b00+b11,m1);
Mat m2(n/2);
vs(n/2,a10+a11,b00,m2);
Mat m3(n/2);
vs(n/2,a00,b01-b11,m3);
Mat m4(n/2);
vs(n/2,a11,b10-b00,m4);
Mat m5(n/2);
vs(n/2,a00+a01,b11,m5);
Mat m6(n/2);
vs(n/2,a10-a00,b00+b01,m6);
Mat m7(n/2);
vs(n/2,a01-a11,b10+b11,m7);
Mat c00(n/2),c01(n/2),c10(n/2),c11(n/2);
c00 =m1+m4-m5+m7;
c01 =m3+m5;
c10=m2+m4;
c11=m1+m3-m2+m6;
for(int i=0;i<n/2;++i)
{
for(int j=0;j<n/2;++j)
{
c.m[i][j]=c00.m[i][j];
c.m[i][j+n/2]=c01.m[i][j];
c.m[i+n/2][j]=c10.m[i][j];
c.m[i+n/2][j+n/2]=c11.m[i][j];
}
}
}
需注意 进行矩阵的运算中,需要对 “+” “-” 运算进行重载!
Mat operator + (Mat &a)
{
Mat c(a.n);
for(int i=0;i<a.n;i++)
{
for(int j=0;j<n;j++)
{
c.m[i][j]=a.m[i][j]+this->m[i][j];
}
}
return c;
}
Mat operator - (Mat &a)
{
Mat c(a.n);
for(int i=0;i<a.n;i++)
{
for(int j=0;j<n;j++)
{
c.m[i][j]=this->m[i][j] -a.m[i][j];
}
}
return c;
}
2)效率分析
普通二分:
T ( n ) = O ( 1 ) T(n)=O(1) T(n)=O(1) $ (n=2)$
T ( n ) = 8 ∗ T ( n / 2 ) T(n)=8*T(n/2) T(n)=8∗T(n/2)+ Θ ( n 2 ) \Theta(n^{2}) Θ(n2) ( n > 2 ) (n>2) (n>2)
故令 a = 8 , b = 2 , f ( n ) = Θ ( n 2 ) 故令 a=8,b=2 ,f(n)=\Theta(n^{2}) 故令a=8,b=2,f(n)=Θ(n2),$ 而n{log_{2}8-1}=n{2}$ ,取 ε = 1 > 0 \varepsilon=1>0 ε=1>0
T ( n ) = Θ ( n 3 ) T(n)=\Theta(n^{3}) T(n)=Θ(n3)
VS算法 (对此问题)
$T(n)=O(1) $
n
=
2
n=2
n=2
T ( n ) = 7 ∗ T ( n / 2 ) + Θ ( n 2 ) T(n)=7*T(n/2)+\Theta(n^{2}) T(n)=7∗T(n/2)+Θ(n2) n > 2 n>2 n>2
故令 a = 7 , b = 2 , f ( n ) = Θ ( n 2 ) , 而 n l o g 2 7 − 1 = n 2 故令a=7,b=2,f(n)=\Theta(n^{2}), 而n^{log_{2}7-1}=n^{2} 故令a=7,b=2,f(n)=Θ(n2),而nlog27−1=n2
f ( n ) = Θ ( l o g 2 7 − 0.8 ) = Θ ( n 2 ) f(n)=\Theta(log_{2}7-0.8)=\Theta(n^{2}) f(n)=Θ(log27−0.8)=Θ(n2) 取 ε = 0.8 > 0 \varepsilon=0.8>0 ε=0.8>0
f(n)=\Theta(n^{2}) , , , 而n{log_{2}8-1}=n{2}$ ,取 ε = 1 > 0 \varepsilon=1>0 ε=1>0
T ( n ) = Θ ( n 3 ) T(n)=\Theta(n^{3}) T(n)=Θ(n3)
VS算法 (对此问题)
$T(n)=O(1) $
n
=
2
n=2
n=2
T ( n ) = 7 ∗ T ( n / 2 ) + Θ ( n 2 ) T(n)=7*T(n/2)+\Theta(n^{2}) T(n)=7∗T(n/2)+Θ(n2) n > 2 n>2 n>2
故令 a = 7 , b = 2 , f ( n ) = Θ ( n 2 ) , 而 n l o g 2 7 − 1 = n 2 故令a=7,b=2,f(n)=\Theta(n^{2}), 而n^{log_{2}7-1}=n^{2} 故令a=7,b=2,f(n)=Θ(n2),而nlog27−1=n2
f ( n ) = Θ ( l o g 2 7 − 0.8 ) = Θ ( n 2 ) f(n)=\Theta(log_{2}7-0.8)=\Theta(n^{2}) f(n)=Θ(log27−0.8)=Θ(n2) 取 ε = 0.8 > 0 \varepsilon=0.8>0 ε=0.8>0
由主定理 T ( n ) = Θ ( n 2.8 ) 由主定理T(n)=\Theta(n^{2.8}) 由主定理T(n)=Θ(n2.8)
4.设计一个算法对n个实数组成的数组进行重新排序,使得其中所有负元素都位于正元素之前。这个算法要兼顾空间效率和时间效率。
考虑快速排序的思想,令数组a[n+1]中,a[0]=0;而后依次读入数据,利用‘0’进行排序
计算模型
存储数列a[n+1],左右下标left、right
代码:
void Sort(vector<int> &a, int n) {
int left = 1;
int right = n;
int pivot = 0;
while (left < right) {
while (left < right && a[left] < pivot) left++;
while (left < right && a[right] >= pivot) right--;
if (left < right) {
swap(a[left], a[right]);
left++;
right--;
}
}
}
int main() {
int n;cin>>n;
vector<int> a(n+1,0);
for(int i=1;i<n+1;++i) cin>>a[i];
Sort(a, n);
for (int i = 1; i <= n; i++) cout << a[i] << " ";
return 0;
}
复杂度:
占用的额外空间为
O
(
1
)
O(1)
O(1),时间复杂度为
O
(
n
)
O(n)
O(n)
5最大子段和
问题分析 使用分治法,则易观察到共有下列三种情况
1)序列的最大子段和与左段最大子段和相同
2)序列的最大子段和与右段最大子段和相同
3)序列的最大子段和横跨两左右段,即左右段的最大子段之和为序列的最大子段
计算模型
存储数列 a[n], 左右下标 left、right
左、右端最大子段和 Ls、Rs
左、右辅助记录字段和s1、s2
代码实现:
int find(int *a, int left, int right)
{
int sum = 0;
if (left == right)
sum = a[left] > 0 ? a[left] : 0;
else
{
int mid = (left + right) / 2;
int Ls = find(a, left, mid);
int Rs = find(a, mid + 1, right);
int s1 = 0;
int lefts = 0;
for (int i = mid; i >= left; i--)
{
lefts += a[i];
if (lefts > s1)
s1 = lefts;
}
int s2 = 0;
int rights = 0;
for (int i = mid + 1; i <= right; i++)
{
rights += a[i];
if (rights > s2)
s2 = rights;
}
sum = s1 + s2;
if (sum < Ls)
sum = Ls;
if (sum < Rs)
sum = Rs;
}
return sum;
}
int main()
{
int n;cin>>n;
int a[n];
for (int i = 1; i <= n; i++) cin>>a[i];
cout<<find(a,1,n);
return 0;
}
时间复杂度:
求取s1,s2 ,时间复杂度 T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n)
递归调用find函数求两侧最大字段和 T ( n ) = O ( l o g n ) T(n)=O(logn) T(n)=O(logn)
则 T ( n ) = o ( n l o g n ) T(n)=o(nlogn) T(n)=o(nlogn)
6.(1)回溯法中,问题的解在有n层解空间树中其解是唯一的吗?是几元组?(2)在回溯法中用什么方法进行剪枝?是怎样执行的?
1)并非唯一 ; 若从0层开始算,则是n元组;若从第一层起算,则是n-1元组;
如下图一个三元组的解,为从第一层起算
2)剪枝方法:判断是否满足约束条件,即使用限界函数进行判断;执行方法:将当前节点标记为死节点;
7.
(1) 装载问题与前面章节所讲述的哪个问题类似?(2) 它们具有什么样的共同特征?(3) 装箱问题还有其他解法吗?请找出至少一种解法并进行比较。
1)0-1背包问题
2)共同特征:都是组合优化类问题;
都有一或两个限界条件:容量/价值的最大/小值;
都有最有目标值:容量/价值的最大/小值;
3)显然,此题可以使用贪心+优先队列或者动态规划法解决
输入:c, n, w[]
输出:最优值maxc和最优解maxx[]
若使用回溯法,则时间复杂度为 O ( 2 n ) O(2^{n}) O(2n) (共 2 n 个节点) (共2^{n}个节点) (共2n个节点)
贪心:
int main() {
int n, c;
cin >> n>>c;
int maxn=0;
priority_queue< int,vector<int>,greater<int> > q;
for (int i = 0; i < n; ++i)
{
int tmp;cin>>tmp;q.push(tmp);
}
cout<<"method:"<<endl;
while(1)
{
maxn+=q.top();
if(maxn>=c) break;
cout<<q.top()<<" ";
q.pop();
}
cout<<endl<<maxn-q.top();
return 0;
}
时间复杂度为 O ( n ) 时间复杂度为O(n) 时间复杂度为O(n)
实际上,在此问题中数据点和限制条件表述并不准确,若只需不超重则有数种方法,若修改数据则能体现此法的优势。
动态规划:
int maxl(int c, vector<int>& w) {
int n = w.size();
vector<vector<int>> dp(n + 1, vector<int>(c + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= c; ++j) {
dp[i][j] = dp[i - 1][j];
if (j >= w[i - 1])
dp[i][j] = max(dp[i][j], dp[i - 1][j - w[i - 1]] + 1);
}
}
return dp[n][c];
}
int main() {
int n, c;
cin >> n >>c;
vector<int> w(n);
for (int i = 0; i < n; ++i) {
cin >> w[i];
}
int maxn = maxl(c, w);
cout << maxn << endl;
return 0;
}
基本思想为:若当前集装箱的重量大于剩余的载重量,则无法装载当前集装箱;否则,尝试装载当前集装箱或者不装载当前集装箱,取两者中的最大值。
时间复杂度:主要的状态转移函数maxl中,时间复杂度为两层循环的复杂度
O
(
n
)
=
(
n
∗
c
)
O(n)=(n*c)
O(n)=(n∗c)