算法——实验三:贪心法_数据、数组、结构体、结构体数组传参

本文探讨了贪心算法的基本思想,通过活动安排和线段覆盖问题阐述其应用。并详细介绍了C++中数组和结构体的传参方式,包括结构体数组的地址传递。在解决活动中,采用快速排序按结束时间排序,并通过贪心策略选择能完成的最多活动。线段覆盖问题同样利用贪心策略,算法时间复杂度为O(nlogn)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组传参和结构体传参:
  1. 单个数据:
    按值传递,也可以地址传递
  2. 数组:
    function(a)有两种传递方法,一种是function(int a[]); 另一种是function(int *a)。这两种两种方法在函数中对数组参数的修改都会影响到实参本身的值!
    C++中数组作为形参进行传递
    数组作为形参时,传递的是地址
  3. 结构体:
    按值传递,也可以地址传递
  4. 结构体数组:
    与数组一样,传递的是地址

结构体:
在这里插入图片描述

结构体元素交换

可以直接交换所有元素

贪心法

贪心法:
基本思想:总是做出在当前看来是最好的选择,期待通过局部最优解获得全局最优解
在这里插入图片描述

题目:活动安排问题

类似这种题还有个区间覆盖问题,就是说很多个区间,其中有些是相互覆盖着的,要求去除多余的区间,使剩下的区间占用长度最大,实际就是这个题,只是问法变换了而已!
在这里插入图片描述

#include <iostream>
// 贪心算法:要求尽可能多的活动,每次选择最早开始最早结束的,用时最少 
using namespace std;
struct active {	// 定义活动的结构体 
	int start;
	int finish;
	int mark;	// 作为此活动有无被执行的标志 /在此处赋初值会有警告 
};
// 选择排序排出最早结束的 
void finishSort(active num[], int N)
{
	int i,j;
	for (i=0; i<N-1; i++) {
		int min = i;
		for (j=i+1; j<N; j++) {
			if (num[j].finish < num[min].finish) {
				min = j;
			}
		}
		// 结构体元素交换 /
		active t = num[i];
		num[i] = num[min];
		num[min] = t;
	} 
}
// 活动安排 
void Select(active num[], int N)
{
	int time = num[0].finish;	// 时间指向第一个活动的结束
	num[0].mark = 1; 
	for (int i=1; i<N; i++) {	// 从第一个活动开始比较 
		if (num[i].start >= time) {	// 此时接着做下一个活动来得及 
			time = num[i].finish;	// 更新时间 
			num[i].mark = 1;	// 标记 
		}
	} 
} 
int main()
{
	active num[100];
//	// 输入
//	int N;
//	cout<<"输入活动个数:"<<endl;
//	cin>>N;
//	for (int i=0; i<N; i++) {
//		cout<<"第"<<i+1<<"个活动:"<<endl<<"起始时间:"<<endl;
//		cin>>num[i].start;
//		cout<<"结束时间:"<<endl;
//		cin>>num[i].finish;
//	} 
	// 默认输入
	int N = 11;
	int s[11] = {1,3,0,5,3,5,6,8,8,2,12};
	int f[11] = {4,5,6,7,8,9,10,11,12,13,14};
	for (int i=0; i<11; i++) {
		num[i].start = s[i];
		num[i].finish = f[i];
		num[i].mark = 0; 
	}
	// 输出
	for (int i=0; i<N; i++) {
		cout<<num[i].start<<" "<<num[i].finish<<" "<<num[i].mark<<endl;
	} 
	// 按照最早结束排序/// 
	finishSort(num, 11);
	// 输出
	for (int i=0; i<N; i++) {
		cout<<num[i].start<<" "<<num[i].finish<<" "<<num[i].mark<<endl;
	} 
	// 活动安排 
	Select(num, 11);
	// 输出
	for (int i=0; i<N; i++) {
		cout<<num[i].start<<" "<<num[i].finish<<" "<<num[i].mark<<endl;
	} 
	// 输出
	cout<<"举办的活动有:"<<endl;
	for (int i=0; i<N; i++) {
		if (num[i].mark == 1) {
			cout<<"第"<<i+1<<"个活动"<<endl;
		}
	} 
	return 0;	
} 

在这里插入图片描述

#include <iostream>
#include <cstdlib>
#include <ctime>
// 贪心算法:要求尽可能多的活动,每次选择最早开始最早结束的,用时最少 
using namespace std;
struct active {	// 定义活动的结构体 
	int start;
	int finish;
	int mark;	// 作为此活动有无被执行的标志 
};
// 快速排序排出最早结束的 
int partition(active num[], int left, int right)
{
	// 设置左右指针
	int i = left;
	int j = right;
	// 设置默认轴值
	int pivot = num[right].finish;  
	while (i != j) { 
		while (i < j && num[i].finish <= pivot) {
			i++;
		}
		while (i < j && num[j].finish >= pivot) {
			j--;
		}
		if (i != j) {	
			active x = num[i];
			num[i] = num[j];
			num[j] = x;
		}
	} 
	active y = num[i];
	num[i] = num[right];
	num[right] = y;
	return i;
} 
void quickSort(active num[], int left, int right)
{
	if (left < right) {
		int mid = partition(num, left, right);
		quickSort(num, left, mid-1);
		quickSort(num, mid+1, right);
	}
}
// 活动安排 
void Select(active num[], int N)
{
	int time = num[0].finish;	// 时间指向第一个活动的结束
	num[0].mark = 1; 
	for (int i=1; i<N; i++) {	// 从第一个活动开始比较 
		if (num[i].start >= time) {	// 此时接着做下一个活动来得及 
			time = num[i].finish;	// 更新时间 
			num[i].mark = 1;	// 标记 
		}
	} 
} 
int main()
{
	srand((unsigned)time(NULL));
	clock_t startTime, finishTime; 
	while (1) {
		// 输入
		int N;
		cout<<"输入活动个数:"<<endl;
		cin>>N;
		active *num = new active[N];
		for (int i=0; i<N; i++) {
			while (1) {
				num[i].start = rand()%100+1;
				num[i].finish = rand()%100+1;
				if (num[i].start < num[i].finish) {
					break;
				}
			}
			num[i].mark = 0;
		} 
		// 输出
		cout<<"自动生成为:"<<endl;
		for (int i=0; i<N; i++) {
			cout<<num[i].start<<" "<<num[i].finish<<" "<<num[i].mark<<endl;
		} 
		// 开始时间
		startTime = clock();
		// 按照最早结束排序
		quickSort(num, 0, N-1);
		// 输出
		cout<<"按照最早结束时间排序后为:"<<endl;
		for (int i=0; i<N; i++) {
			cout<<num[i].start<<" "<<num[i].finish<<" "<<num[i].mark<<endl;
		}
		// 活动安排 
		Select(num, N);
		// 结束时间
		finishTime = clock(); 
		// 输出
		cout<<"活动安排后为:"<<endl;
		for (int i=0; i<N; i++) {
			cout<<num[i].start<<" "<<num[i].finish<<" "<<num[i].mark<<endl;
		} 
		// 输出
		cout<<"举办的活动有:"<<endl;
		for (int i=0; i<N; i++) {
			if (num[i].mark == 1) {
				cout<<"第"<<i+1<<"个活动"<<endl;
			}
		} 
		cout<<"时间为:"<<(double)(finishTime-startTime)/CLOCKS_PER_SEC<<"s"<<endl;
		delete[] num;
		// 判断结束输入
		int choose; 
		cout<<"继续输入请按1,否则按其他数字:";
		cin>>choose; 
		if (choose == 1) {
			break;
		}
	}
	return 0;	
} 
题目:线段覆盖问题

在这里插入图片描述

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
struct line {
	int begin;
	int end;
};
// 选择排序 
void beginSort(line num[], int N)
{
	int i,j;
	for (i=0; i<N-1; i++) {
		int min = i;
		for (j=i+1; j<N; j++) {
			if (num[j].begin < num[min].begin) {
				min = j;
			}
		}
		// 结构体元素交换 /
		line t = num[i];
		num[i] = num[min];
		num[min] = t;
	} 
}
int Cover(line num[], int N)
{
	int mark = num[0].end;	// 设置标志
	int len = num[0].end-num[0].begin;
	for (int i=1; i<N; i++) {	// 从1开始 
		// 分三种情况
		// 下一个线段全在mark外 
		cout<<endl;
		cout<<mark<<" "<<len<<endl;
		
		if (num[i].begin >= mark) {
			len += num[i].end-num[i].begin;
			mark = num[i].end;	// 注意顺序!!!!!!!!!! 
		} 
		// 下一个线段部分在mark里 
		else if (num[i].begin < mark && num[i].end > mark) {
			len += num[i].end-mark;
			mark = num[i].end;
		}
		// 下一个线段全在mark里
		// mark和len不变 
		cout<<mark<<" "<<len<<endl;
	}
	return len; 
} 
int main()
{
	srand((unsigned)time(NULL));
	line num[100];
	int N;
	cout<<"输入线段个数:"<<endl;
	cin>>N;
	// 赋值
	for (int i=0; i<N; i++) {
		while (1) {
			num[i].begin = rand()%10+1;
			num[i].end = rand()%10+1;
			if (num[i].begin < num[i].end) {
				break;
			}
		}
	} 
	// 输出
	for (int i=0; i<N; i++) {
		cout<<num[i].begin<<" "<<num[i].end<<endl;
	} 
	// 按照开始坐标由小到大的排序 
	beginSort(num, N); 
	// 输出
	for (int i=0; i<N; i++) {
		cout<<num[i].begin<<" "<<num[i].end<<endl;
	}
	cout<<"开始执行Cover"<<endl; 
	cout<<endl<<"覆盖长度为:"<<Cover(num, N)<<endl;
	return 0;
}

在这里插入图片描述

注意有返回值函数的调用顺序
在这里插入图片描述

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
struct line {
	int begin;
	int end;
};
// 选择排序 
void beginSort(line num[], int N)
{
	int i,j;
	for (i=0; i<N-1; i++) {
		int min = i;
		for (j=i+1; j<N; j++) {
			if (num[j].begin < num[min].begin) {
				min = j;
			}
		}
		// 结构体元素交换
		line t = num[i];
		num[i] = num[min];
		num[min] = t;
	} 
}
// 计算覆盖长度 
int Cover(line num[], int N)
{
	int mark = num[0].end;	// 设置标志
	int len = num[0].end-num[0].begin;
	for (int i=1; i<N; i++) {	// 从1开始 
		// 分三种情况
		// 下一个线段全在mark外 
		if (num[i].begin >= mark) {
			len += num[i].end-num[i].begin;
			mark = num[i].end;	 
		} 
		// 下一个线段部分在mark里 
		else if (num[i].begin < mark && num[i].end > mark) {
			len += num[i].end-mark;
			mark = num[i].end;
		}
		// 下一个线段全在mark里
		// mark和len不变 
	}
	return len; 
} 
int main()
{
	srand((unsigned)time(NULL));
	clock_t startTime, finishTime; 
	while (1) {
		int N;
		cout<<"输入线段个数:"<<endl;
		cin>>N;
		line *num = new line[N];
		// 赋值
		for (int i=0; i<N; i++) {
			while (1) {
				num[i].begin = rand()%100+1;
				num[i].end = rand()%100+1;
				if (num[i].begin < num[i].end) {
					break;
				}
			}
		} 
		// 输出
		cout<<"随机生成为:"<<endl;
		for (int i=0; i<N; i++) {
			cout<<num[i].begin<<" "<<num[i].end<<endl;
		} 
		// 开始计时 
		startTime = clock(); 
		// 按照开始坐标由小到大的排序 
		beginSort(num, N); 
		// 输出
		cout<<"按照开始坐标由小到大的排序后为:"<<endl;	
		for (int i=0; i<N; i++) {
			cout<<num[i].begin<<" "<<num[i].end<<endl;
		}
		 计算覆盖长度 
		int myLength = Cover(num, N);
		// 结束计时 
		finishTime = clock(); 
		cout<<endl<<"覆盖长度为:"<<myLength<<endl;
		cout<<"时间为:"<<(double)(finishTime-startTime)/CLOCKS_PER_SEC<<"s"<<endl;
		delete[] num;
		// 判断结束输入
		int choose; 
		cout<<"继续输入请按1,否则按其他数字:";
		cin>>choose; 
		if (choose == 1) {
			break;
		}
	}
	return 0;
}
总结:

由于较早结束的活动也是开始时间较早的,活动结束越早,越有可能在有限时间内做完更多活动。所以使用快速排序按照活动结束时间非减序排列。假设第一个活动必然做,设置时间的标志time指向最后一个必然做的活动的结束时间,从此活动开始,与下一个活动的开始时间比较。若下一个活动开始时间大于等于时间标志,说明此时两活动相容,下一个活动必然做。更新时间标志,循环比较完所有活动。
快速排序的时间复杂度是O(nlogn),判断活动是否做时,要依次考察每一个活动,其时间复杂度是O(n),因此,算法的时间复杂度是O(nlogn)。
首先设一个序列X,X按照结束时间非减序排列,由于X第一个活动X1结束时间最早,所以它必然被选到。假设有一个最优解A,A中活动也是按照结束时间非减序排列,设第一个活动为活动k,若k=1,那么A就是一个以贪心选择开始的最优解;若k!=1,那么设一个解为B,B中不包含A中的这个活动k,但是包含A中的其他活动,且B的第一个活动是X1。所以由X的排序可以知道,活动X1的结束时间比活动k早,所以B也是一个以贪心选择开始的最优解。所以总存在一个以贪心选择开始的最优解。当第一个活动已经确定,对于剩下的活动,每一次所做的贪心选择都将原问题简化为与原问题具有相同形式的子问题。通过数学归纳法可知,贪心选择算法最终产生原问题的最优解。
在这里插入图片描述
线段覆盖问题:
在这里插入图片描述
快速排序的时间复杂度是O(nlogn),判断线段是否覆盖时,要依次考察每一个线段,其时间复杂度是O(n),因此,算法的时间复杂度是O(nlogn)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值