算法期末作业

实验1

q1.请实现如下描述的快速选择算法:函数原型 void select(int a[],int beg,int end,int k)//在数组a的beg...end范围内选择最小的k个的元素并将其存于数组a的 beg....beg+k-1位置。
要求:
(1)采用分治算法:(递归程序)
区间beg...end内的选择k小问题定义为:
    1.划分算法
    2.根据划分位置p,决定三者之一(p-beg为左边元素个数):
        a.如果p-beg=k或p-beg+1=k,什么也不做;
        b.如果p-beg>k,求select(a,beg,p-1,k)
        c.如果p-beg+1<k,求select(a,p+1,end,k-l-p+beg)
    3.退回上级
(2)划分算法要求采用如下形式:int simplepart(int a[],int beg, int end)//返回划分后枢轴所在位置
    1.i设为beg,j设为beg+1;
    2.是否j<=end,如果不是,跳转到8
    3.是否a[j]<a[beg],如果不是跳转到6
    4.i自增
    5.交换a[i]和a[j]
    6.j++
    7.jmp 2
    8.交换a[i]和a[beg]
    9.返回i,程序结束

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long klong
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
int simplepart(int a[],int beg, int end)
{
	int i = beg, j = beg + 1;
	while(j <= end)
	{
		if(a[j] < a[beg]) 
		{
			i++;
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;
		}
		j++;
	}
	int temp = a[j];
	a[j] = a[beg];
	a[beg] = temp;
	return i;
	
}
void select (int a[], int beg, int end, int k)
{
	int p = simplepart(a,beg,end);
	if(p - beg > k )
	{
		select(a,beg,p-1,k);
	}
	else if(p - beg + 1 < k)
	{
		select(a,p+1,end,k-1-p+beg);
	}
}
int main()
{
	int a[] = {0,4,6,48,4,6,74,231,5,23,1,5,641,85,2,4,6,4,85,4,5,4,78,45,12};//25个数
	select(a,0,24,10);
	for(int i = 1; i < 25; i++) 
	{
		printf("%d ",a[i]);
	}
	return 0;
}

(3)递归程序需要处理各种特殊情况,例如beg>end(元素个数为0),区间中元素个数不大于k,
(4)需要生成一个稍大的数组测试,并且在程序中增加一个统计元素比较次数的功能。
------------------------------------------------------------
q2.请编写算法求解如下问题:使用分治法在一个整型数组中去重。函数原型:int unique(int a[],int beg,end) 返回值为剩余元素个数
算法要求:
(1)采用分治法(递归程序);
区间beg...end内的去重(并排序)定义为
    1.区间beg...mid的去重(并排序)问题
    2.区间mid+1...end的去重(并排序)问题
    3.有序表合并(可采用去一次重复版本:即在标准合并算法的基础上,遇到两个相同元素,只复制一个,但指针都后移),空间复杂度O(n),时间复杂度O(n)

(2)需要在主函数中生成一个规模和重复度均较大的数组测试。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
int unique(int a[],int beg,int end)
{
	if(beg >= end)
	{
		return 1;
	}
	int mid = (beg + end) >> 1;
	int len1 = unique(a,beg,mid);
	int len2 = unique(a,mid + 1, end);
	int len = len1 + len2;
	int diff = end - beg + 1 - len;
	int i = beg, j = mid + 1;
	int nums = 0;
	int data[1000];
	for(int i = beg; i <= end; i++)
	{
		data[i] = a[i];
	}
	for(int index = beg - diff; index + nums <= end; index++)
	{
		if(i >= mid + 1)
		{
			a[index] = data[j];
			j++;
		}
		else if(j > end)
		{
			a[index] = data[i];
			i++;
		}
		else if(data[i] < data[j])
		{
			a[index] = data[i];
			i++;
		}
		else if(data[i] > data[j])
		{
			a[index] = data[j];
			j++;
		}
		else
		{
			a[index] = a[i];
			i++;
			j++;
			nums++;
		}
	}
	return end - beg + 1 - nums;
}
int main()
{
	int a[] = {3, 1, 2, 2, 5, 4};
	int len = unique(a,0,5);
	for(int i = 0; i <= 5; i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
	return 0;
 } 


1.请参照附件“编程珠玑——最大子段和”中8.4介绍的算法,实现最大子段和问题(最短长度为0)的动态规划解法,要求:
(1)定义子问题:d(i)“截至于a[i]的最大子段和” 
d(0)=max(0,a(0))
i>0时,d(i)=max(0,d(i-1)+a(i))
(2)只用一个单重循环完成以上算法(求最优值和最优解合到一个循环中),时间复杂度O(n),空间复杂度O(n)
(3)对某个实例运行算法验证正确性

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long 
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
int main()
{
	int data[] = {0,1,5,4,-4,8,4,90,4,-45,-2};
	int dp[10];
	int n = 10;
	dp[1] = data[1] > 0 ? data[1] : 0;
	for(int i = 2; i <= n; i++)
	{
		dp[i] = max(dp[i-1] + data[i],dp[i-1]);
	}
	printf("%d\n",dp[n]) ;
	
	
	return 0;
}


2.请实现0-1背包问题:
有n个物品,存于数组t中,物品i(i=0...n-1)的重量为t[i].w,其价值为t[i].v。
现有一个容量为C的背包,请求出放入背包中的物品的组合,使其总价值最大。
要求:
(1)使用如下定义的动态规划解法:
定义子问题d(i,j)(i=0...n,j=0...C):  只有物品i,i+1...n-1,剩余背包容量为j的背包问题的最优值
d(n,j)=0(没有任何物品)
当i<n时:
如果t[i].w<j,d(i,j)=d(i+1,j);
否则d(i,j)=max(d(i+1,j),d(i+1,j-t[i].w)+t[i].v)
(2)将d(i,j)存于二维数组中,使用循环求解,可参见附件中的代码,但是该代码并不符合要求(1)
(3)写程序逆推求出最优解,
(4)对某个实例运行算法验证正确性

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
 
const int INF = 2005;
int d[INF][INF];	//定义状态d[i][j]为前i件物品放入容量为j的背包时的最大价值;
int W[INF],C[INF];
 
 
int main()
{
	int n, v;
	scanf("%d%d",&n,&v);
		
	for(int i = 1; i <= n; i++)
	{
		scanf("%d",&W[i]);
	}
		
	for(int i = 1; i <= n; i++)
	{
		scanf("%d",&C[i]);
	}
		
	memset(d,0,sizeof(d));	
		
	for(int i = 1; i <= n; i++)	
	{
		for(int j = 0; j <= v; j++)
		{
			if(j >= C[i])
			{
				d[i][j] = max(d[i-1][j],d[i-1][j - C[i]] + W[i]);
			}
			else
			{
				d[i][j] = d[i-1][j];
			}
		}
	}
	printf("%d\n",d[n][v]);
	return 0;
} 

 

请参照附件中“活动安排问题”的描述,编写算法实现。

有n个任务需要n个人完成,每人恰好完成一个任务,这些任务同时开始。
第i个人(i=0...n-1)完成第j个任务的时间是t(i,j),第i个人分配的任务记为s(i),则完成时间为t(i,s(i)),则全部任务结束时间为 et=max(t,s(i)),i=0...n-1。
选择合适的任务分配方案,使et在所有方案中最小。

要求:
1.随机生成t数组
2.使用回溯法求解这一问题,且把问题抽象为排列树上的搜索问题(按照i升序,依次试探s(i));
3.采用如下剪枝方案:当已经试探s(0)~s(i-1)时,判定该分支是否需要访问
已知 : curmax=max(t(j,s(j)))  j=0~i-1,即 前面那些选择 的最大时间,一般通过递归参数传入
已知:对所有  k=0~n-1:    mint(k)=t(k,j),j=0~n-1,即 mint是 一维数组,是t按行求最小值得出的。可预先计算得到。它的作用是给出后面某一步选择的时间的下限。
已知:对所有  k=0~n-1:    maxmint(k)=max(mint(j)),j=k~n-1,即maxmint是mint从k到n-1部分的最大值。可预先计算得到。
已知:当前最优解的完成时间  curmin
剪枝条件:若   max(curmax,maxmin(i))>=curmin    则该分支不必访问。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N = 105;
int s[N],mint[N],maxmint[N];
int t[N][N] = {
			{},
			{0,1,4,23,1,7},
			{0,4,8,7,1,15},
			{0,7,1,1,4,4},
			{0,3,4,4,8,24},
			{0,45,12,7,14,5}
		  };
int mark[N],n = 5;
int dfs(int i, int curmax) 
{
	if(i == n + 1)
	{
		return curmax;
	}
	else
	{
		int max1 = 0x3f3f3f3f;
		for(int j = 1; j <= n; j++)if(mark[j] == 0)
		{
			if(max(curmax,maxmint[i]) >= t[i][j])
			{
				mark[j] = 1;
				int temp = dfs(i + 1, max(curmax,t[i][j]));
				max1 = min(max1, temp);
				mark[j] = 0;
			}
		}
		return max1;
	}

}
int main()
{
	for(int i = 1; i <= n; i++)
	{
		int temp = 0x3f3f3f3f;
		for(int j = 1; j <= n; j++)
		{
			temp = min(t[i][j],temp);
		}
		mint[i] = temp;
		maxmint[n] = mint[n];
		for(int i = n - 1; i >= 1; i--)
		{
			maxmint[i] = max(mint[i],maxmint[i + 1]);
		}	
	}
	int res = dfs(1,0);
	printf("%d\n",res);
	return 0;
} 

可自行搜索“最大值最小化问题”、“旅行商问题”、“任务分配问题”等,帮助理解其思路。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值