算法设计与分析 5.1 分治、二分查找、大数乘法、V.Strassen、最大子段和

在这里插入图片描述

在这里插入图片描述

请用分治算法求解一个包含 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(21)

则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)=3T(n/2)+cn

a = 3 , b = 2 , f ( n ) = c ∗ n a=3,b=2 ,f(n)=c*n a=3,b=2,f(n)=cn, n^{log_2 3-log_2 3/2} = n, f ( n ) = f(n)= f(n)= img

则取 ε = 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)=8T(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)=7T(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),nlog271=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)=Θ(log270.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)=7T(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),nlog271=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)=Θ(log270.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)=(nc)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值