算法设计与分析

  王晓东著《计算机算法设计与分析》第五版习题

目录

  • 第一次作业
    • 例2-1 阶乘函数
    • 例2-2Fibonacci数列
    • 例2-5整数划分问题
    • 例2-6Hanoi问题
  • 第二次作业
    • 二分搜索技术
    • 改进后的二分搜索法(课本p39 2-3)
    • 改进的合并排序
    • 习题2-3改写二分搜索算法
  • 第三次作业
    • O(1)空间合并算法
    • O(1)空间合并算法(另解)
    • Hoare版本递归-快速排序
    • Hoare版本非递归-快速排序
  • 第四次作业
    • 捡拾硬币问题
    • 最大子段和(书上解法)
    • 最长公共子序列(书上解法)
    • 最长公共子序列
    • 最长公共子序列(另解)
  • 第五次作业
    • 电路布线
    • 电路布线(另解)
    • 0-1背包
    • 0-1背包(另解)
    • 电路布线(优化)
    • 0-1背包问题(优化)
    • 长江游艇问题
    • 长江游艇(优化)
  • 第六次作业
    • 最优服务次序问题
    • 删数问题
    • 活动安排问题
    • 背包问题
    • 最短服务次序问题
  • 第七次作业
    • N皇后问题
    • 回溯法 0-1背包
    • 回溯 最优装载
    • 连续邮资问题


第一次作业

例2-1 阶乘函数

#include
#include 
int main(){
	int n=0;
	printf("输入数字:");
	scanf("%d",&n);
	if(n<0)
		printf("请输入大于0的数!\n");
	int exam=n-(int)n;
	if(exam!=0)
		printf("请输入整数!\n") ;
	printf("%d的阶乘是%d\n",n,factorial(n));
} 
int factorial(int n){
	if(n==0)
		return 1;
	else
		return n*factorial(n-1);
}

例2-2Fibonacci数列

#include
#include
int main(){
	int n=0;
	printf("请输入斐波那契数列的第几项:\n");
	scanf("%d",&n);
	printf("斐波那契数列的第%d项是%d",n,fibonacci(n));
}
int fibonacci(int n){
	if(n<=2)
		return 1;
	else
		return fibonacci(n-1)+fibonacci(n-2);
}

例2-5整数划分问题

#include
#include
int main(){
	int n=0,m=0;
	printf("请输入想求的正整数的值和其不大于何个正整数的划分\n");
	printf("(以空格分界)\n"); 
	scanf("%d %d",&n,&m);
	printf("正整数%d不大于%d的划分个数为%d个",n,m,q(n,m));
}
int q(int n,int m){
	if((n<1)||(m<1))
		return 0;
	if((n==1)||(m==1))
		return 1;
	if(n<m)
		return q(n,n);
	if(n==m)
		return q(n,m-1)+1;
	return q(n,m-1)+q(n-m,m);
}

例2-6Hanoi问题

#include
#include
int move(int a,int b){
	printf("%d->%d\n",a,b);
	return 0; 
}
void hanoi(int n,int a,int b,int c){
	if(n>0){
		hanoi(n-1,a,c,b);
		move(a,b);
		hanoi(n-1,c,b,a); 
	}
}
int main(){
	printf("此为汉诺塔问题\n");
	printf("请输入数字表示想要解决的汉诺塔问题中的总盘子数目\n");
	printf("并顺次输入数字以代替起始柱、目标柱和中转柱\n"); 
	int n=0,a=0,b=0,c=0; 
	scanf("%d %d %d %d",&n,&a,&b,&c);
	printf("此问题的解决步骤为:\n");
	hanoi(n,a,b,c); 
}



第二次作业

二分搜索技术

#include
int BinarySearch(int a[],int x,int n){
//在数组中找到X在其中的位置,并返回下标
int left=0;
int right=n-1;
while(left<=right-1){
	int middle=(left+right)/2;
	if(x==a[middle])
		return middle;
    if(x>a[middle])
    	left=middle;
    else
    	right=middle;
	  
} 
if(x==a[left])
   return left;
else
   return-1;							//未找到X 
}
int main()
{	int a[]={0,1,2,3,4,5,6,7,8,9};
	int k=BinarySearch(a,6,10); 
	printf("%d",k);
}

改进后的二分搜索法(课本p39 2-3)

//改进后的二分搜索法(课本p39 2-3) 
#include

int BinarySearch(int a[],int x,int l,int r){
	int m;
	int n=r;		//存储最后的数组元素下标 
	if(x<a[0]){
		printf("%d不在数组中,它比数组中所有元素都小\n",x);
	}
	else if(x>a[n]){
		printf("%d不在数组中,它比数组中所有元素都大\n",x);
	}
	else{
		while(r>=l){
			m=(l+r)/2;
			if(x==a[m]) {
				return m;
			}
			else if(x<a[m]) {
				r=m-1;
			}
			else{
				l=m+1;
			}
		}
	    printf("%d不在数组中,与它相邻的左右元素下标为%d和%d",x,r,r+1);
	}
	return -1;
}
int main(){
	int x,l,r,n;
	int array[]={2,3,5,7,11,13,17,19,23,29};
	l=0;
	r=9;
	printf("请输入要查找的整数x:");
	scanf("%d",&x);
	n=BinarySearch(array,x,l,r);
	if(n!=-1){
		printf("%d所在的数组下标是%d\n",x,n);
	}
	return 0;
}

改进的合并排序

#include
#include
//基于上课所讲,合并排序改进(非递归算法) 
void MergeSort(int *list,int length){
	int i,left_min,left_max,right_min,right_max,next;
	int *tmp=(int*)malloc(sizeof(int) * length);
	for(i=1;i<length;i*=2){
		for(left_min=0;left_min<length-i;left_min=right_max){
			right_min=left_max=left_min+i;
			right_max=left_max+i;
			if(right_max>length)
				right_max=length;
			next=0;
			while(left_min<left_max&&right_min<right_max)
				tmp[next++]=list[left_min]>list[right_min]?list[right_min++]:list[left_min++];	
			while(left_min<left_max)
				list[--right_min]=list[--left_max];
			while(next>0)
				list[--right_min]=tmp[--next];
		}
	}
} 
int main(int argc,char *argv[]){
	int i;
	int array[]={49,38,65,97,76,13,27};
	MergeSort(array,7);
	for(i=0;i<7;i++){
		printf("%d ",array[i]);
	}	
	printf("\n");
	return 0;
} 

习题2-3改写二分搜索算法

#include
#include
//基于P17页二分搜索技术 
int BinarySearch(int a[],int x,int n){
	int i=0;//i元素为小于x的最大元素位置 
	int j=0;//j元素为大于x的最小元素位置 
	//在a[0]<=a[1]<=...<=a[n-1]中搜索x
if(n>0&&x>=a[0]){
	int left=0;
	int i=0; 
	int right=n-1;
	int j=n-1;
	while(left<=right){//这里有left=right的判断,可在进入循环体的第一个if处跳出 
	int middle=(left+right)/2;
		if(x==a[middle]){
			i=middle;
			j=middle; 
			return middle;//仅有这一个return语句 
		}
		if(x>a[middle]){
			left=middle+1;//a[middle]的值被略过,因为已经比较了x和a[middle]的大小
			i=middle+1; 
		}		
		else{				//x
			right=middle-1;//a[middle]的值同样被略过,因为已比较了x和a[middle]的大小
			j=middle-1; 
		}
	  }//(left<=right)的判断 
	//该层为为找到的情况
	return i;//返回小于x的最大元素位置  
	}//if(n>0&&x>=a[0])的判断 
}//函数体 
int main(){	
	int a[]={0,1,2,3,4,5,6,7,8,9};
	float x=0;//这里x不能用整型,用int直接无条件跳转外else判断语句 
	printf("请输入要在0-9内查找的数字:\n");
	scanf("%f",&x);
	if(x>=0&&x<=9){
	float pd=x-(int)x; //这里pd也不能用整型,用int直接无条件跳转内else判断语句 
		if(pd==0)
			printf("查找的数字在队列中排第%d位\n",BinarySearch(a,x,10)+1);
		else{
			printf("在0-9队列范围中但未找到该数字!\n"); 
			printf("小于x的最大元素下标为第%d位\n",BinarySearch(a,x,10));
			printf("大于x的最小元素下标为第%d位\n",BinarySearch(a,x,10)+1);
		}
	}
	else{
		printf("所查找元素未在0-9队列范围内!\n"); 
		if(BinarySearch(a,x,10)==0)
			printf("大于x的最小元素下标为第%d位\n",BinarySearch(a,x,10));
		else
			printf("大于x的最小元素下标为第9位\n");
	}
	return 0;
}

第三次作业

O(1)空间合并算法

//设子数组a[0:k-1]和a[k:n-1]已排好序(0<=k<=n-1)
//试设计一个合并这2个子数组为排好序的数组a[0:n-1]的算法
//要求算法在最坏情况下所用的计算时间为O(n),且只用到O(1)的辅助空间 
//循环移位合并算法-向右循环移位合并
//向右循环移位合并算法首先用二分搜索算法在数组段a[k:n-1]中搜索a[0]的插入位置
//即找到位置p使得a[p]
//使a[k-1]移动到a[p]的位置,直至只剩下一个数组段
//此时整个数组已排好序 
//对剩余的数组元素重复上述过程,直至只剩下一个数组段,此时整个数组都已排好序 
#include
#include

void mergefor(int a[],int k,int n)
{//Merge a[0:k-1] and a[k:n-1]
	int i=0,j=k;
	while(i<j&&j<n){//保证左数组a[0:k-1]和右数组a[k:n-1]至少有一个以上的元素 
		int p=binarySearch(a,a[i],j,n-1);
		shiftright(a,i,p,p-j+1);//向右循环移位 
		j=p+1; i+=p-j+2;
	}
}
int binarySearch(int a[],int *x,int left,int right)
{//二分搜素,用于在数组段a[left:right]中搜索元素x的插入位置 
	int middle;
	while(left<=right){
		middle=(left+right)/2;
		if(x==a[middle])
			return middle;
		if(x>a[middle])
			left=middle+1;
		else
			right=middle-1;
	}
	if(x>a[middle])
		return middle;
	else
		return middle-1;
}
void shiftright(int a[],int s,int t,int k){//用于将数组段a[s:t]中元素循环右移位k个位置 
	int i; 
	for(i=0;i<k;i++){
		int tmp=a[t];
		int j;
		for(j=t;j>s;j--)
			a[j]=a[j-1];
		a[s]=tmp;
	}
} 

//上述算法中,数组段a[0:k-1]中元素的移动次数不超过k次,数组段a[k:n-1]中元素最多移动1次。
//因此,算法的元素移动总次数不超过k方+(n-k)次
//算法元素的比较次数不超过klog(n-k)次
//当k<根号n时,算法的计算时间为O(n)
//而当k=O(n)时,算法的计算时间为O(n方)
//由于数组段循环右移算法只用了O(1)的辅助空间,所以整个算法所用的辅助空间为O(1) 

int main(){
	int a[]={15,34,65,21,34,54};
	int i; 
	printf("合并前的数组为:\n");
	for (i =0; i < 6; i++)//输入合并前的数组 
		printf("%d ", a[i]);
	printf("\n");
	mergefor(a,3,6);
	printf("合并后的数组为:\n");
	for (i =0; i < 6; i++)//输出合并后的数组 
		printf("%d ", a[i]);
}

O(1)空间合并算法(另解)

//2-9.O(1)空间合并算法(向右循环换位合并) 
#include 

int binarySearch(int *a,int x,int l,int r) {		
//改进的二分搜索,搜索a[0]的插入位置,本函数返回p,a[0]在p和p+1之间 
    int m;
    while (l<=r){
        m=(l+r)/2;
        if (x==a[m])
            return m;
        if (x>a[m]) {
            l=m+1;
        }
		else{
            r=m-1;
        }
    }
    if (x>a[m])
        return m;
    else{
    	return m-1;
	}
}

void shiftRight(int *a,int s,int t,int k) {
//将数组a[s...t]向右循环移动k个位置 
    for (int i=0;i<k;i++) {
        int temp=a[t];
        for (int j=t;j>s;j--) {
            a[j]=a[j-1];
        }
        a[s]=temp;
    }
}

void mergeArray(int *a,int k,int n){
    int i=0;
    int j=k;
    while (i<j && j<n){
        int x=a[i];
        int p=binarySearch(a,x,j,n-1);
        shiftRight(a,i,p,p-j+1);	//将数组前半a[0...p]向右循环换位p-k+1个位置,此时a[k-1]移动到a[p]的位置 
        j=p+1;
        i+=p-j+2;
    }
}

int main(){
    int a[]={17,19,23,29,2,3,5,7,11,13};
    int i; 
    mergeArray(a,4,10);
	for(i=0;i<10;i++){
		printf("%d  ",a[i]);
	}
    return 0;
}

Hoare版本递归-快速排序

//Hoare版本的单趟排序的基本步骤如下:
//1、选出一个key,一般是最左边或是最右边的。
//2、定义一个L和一个R,L从左向右走,R从右向左走。
//(需要注意的是:若选择最左边的数据作为key,则需要R先走;
//若选择最右边的数据作为key,则需要L先走)。
//3、在走的过程中,若R遇到小于key的数,则停下,L开始走,
//直到L遇到一个大于key的数时,将L和R的内容交换,R再次开始走,
//如此进行下去,直到L和R最终相遇,此时将相遇点的内容与key交换即可。
//(选取最左边的值作为key)
//经过一次单趟排序,最终使得key左边的数据全部都小于key,key右边的数据全部都大于key。
//然后我们在将key的左序列和右序列再次进行这种单趟排序,
//如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,
//便停止操作,因为这种序列可以认为是有序的。
#include
#include
//快速排序(Hoare版本)
void Swap(int* p1, int* p2){//传地址,实现交换 
	int tmp;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void QuickSort1(int* a, int begin, int end)//第一个参数为数组,第二个为数组起始元素下标,第三个为数组末尾元素下标 
{
	if (begin>= end)//当只有一个数据或是序列不存在时,不需要进行操作
		return;
		
	int left = begin;//L
	int right = end;//R
	int keyi = left;//key的下标
	while (left < right)
	{
		//right先走,找小
		while (left < right&&a[right] >= a[keyi])//若元素值大于key,继续向中间移动不进行对换操作 
		{
			right--;
		}
		//left后走,找大
		while (left < right&&a[left] <= a[keyi])//若元素值小于key,继续向中间移动不进行对换操作
		{
			left++;
		}
		if (left < right)//若这时满足左标记在右标记左边,则交换left和right的值
		{
			Swap(&a[left], &a[right]);
		}
	}
	int meeti = left;//L和R的相遇点
	Swap(&a[keyi], &a[meeti]);//交换key和相遇点的值

	QuickSort1(a, begin, meeti - 1);//key的左序列进行此操作
	QuickSort1(a, meeti + 1, end);//key的右序列进行此操作
}

int main(){
	int a[]={15,34,65,21,54,34};
	int i; 
	printf("排序前的数组为:\n");
	for (i =0; i < 6; i++)//输入排序前的数组 
		printf("%d ", a[i]);
	printf("\n");
	QuickSort1(a,0,5);//调用快速排序函数 
	printf("排序后的数组为:\n");
	for (i =0; i < 6; i++)//输出排序后的数组 
		printf("%d ", a[i]);
}

Hoare版本非递归-快速排序

#include
#include
#include "Stack.h"
//当我们需要将一个用递归实现的算法改为非递归时,一般需要借用一个数据结构,那就是栈。
//将Hoare版本、挖坑法以及前后指针法的快速排序改为非递归版本,其实主体思想一致,只是调用的单趟排序的算法不同而已。
//于是我们可以先将Hoare版本、挖坑法和前后指针法的单趟排序单独封装起来。
//然后写一个非递归的快速排序,在函数内部调用单趟排序的函数即可。

//Hoare版本(单趟排序)
//快速排序的非递归算法基本思路:
//1、先将待排序列的第一个元素的下标和最后一个元素的下标入栈。
//2、当栈不为空时,读取栈中的信息(一次读取两个:一个是L,另一个是R),
//然后调用某一版本的单趟排序,排完后获得了key的下标,然后判断key的左序列和右序列是否还需要排序,
//若还需要排序,就将相应序列的L和R入栈;若不需排序了(序列只有一个元素或是不存在),就不需要将该序列的信息入栈。
//3、反复执行步骤2,直到栈为空为止。

//初始化
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}
//销毁
void StackDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
//入栈--进栈--压栈
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//判断能否需要扩容
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->a =(STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
//出栈
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}
//判断是否为空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
//栈的大小
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
//栈顶
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

void Swap(int* p1, int* p2){//传地址,实现交换 
	int tmp;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

int PartSort1(int* a, int left, int right)
{
	int keyi = left;//将key的下标设置为left 
	while (left < right)
	{
		//right走,找小
		while (left < right&&a[right] >= a[keyi])
		{
			right--;
		}
		//left先走,找大
		while (left < right&&a[left] <= a[keyi])
		{
			left++;
		}
		if (left < right)
		{
			Swap(&a[left], &a[right]);//交换left和right的值
		}
	}
	int meeti = left;//L和R的相遇点
	Swap(&a[keyi], &a[meeti]);//交换key和相遇点的值
	return meeti;//返回相遇点,即key的当前位置
}

//快速排序(非递归实现)
void QuickSortNonR(int* a, int begin, int end)
{
	ST st;//创建栈
	StackInit(&st);//初始化栈
	StackPush(&st, begin);//待排序列的L
	StackPush(&st, end);//待排序列的R
	while (!StackEmpty(&st))
	{
		int right = StackTop(&st);//读取R
		StackPop(&st);//出栈
		int left = StackTop(&st);//读取L
		StackPop(&st);//出栈
		//该处调用的是Hoare版本的单趟排序
		int keyi = PartSort1(a, left, right);
		if (left < keyi - 1)//该序列的左序列还需要排序
		{
			StackPush(&st, left);//左序列的L入栈
			StackPush(&st, keyi - 1);//左序列的R入栈
		}
		if (keyi + 1 < right)// 该序列的右序列还需要排序
		{
			StackPush(&st, keyi + 1);//右序列的L入栈
			StackPush(&st, right);//右序列的R入栈
		}
	}
	StackDestroy(&st);//销毁栈
}

int main(){
	int a[]={15,34,65,21,54,34};
	int i; 
	printf("排序前的数组为:\n");
	for (i =0; i < 6; i++)//输入排序前的数组 
		printf("%d ", a[i]);
	printf("\n");
	QuickSortNonR(a,0,5);//调用快速排序函数 
	printf("排序后的数组为:\n");
	for (i =0; i < 6; i++)//输出排序后的数组 
		printf("%d ", a[i]);
}

第四次作业

捡拾硬币问题

//现有n个硬币按顺序依次排列在你面前,可以看为一个数组coins[]={5,1,2,10,6,2}
//请在此中捡取若干个硬币,使得所取硬币累加值最大,捡取个数不限,但相邻两个硬币不得捡取
//请设计相应算法,并输出累加值
//提示:硬币面值必须是正数,不能有负值。建立数组dp[i]存储选取前i个硬币的累加值
//动态规划算法 
#include
#include
#define N 6
int j=0;
int selectcoins(int c[],int n,int s[])
{
	int i,dp[n];
	dp[0]=0;//只有0个硬币时dp自然为0 
	dp[1]=c[0];//只有1个硬币,dp=c[0] 
	s[j]=1;//因为dp[1]=c[0],暂时选择了第一个硬币,所以暂时给s[0]赋值为1 
	for(i=2;i<=n;i++)
	{	
		//dp[i]的值却决于新添加的硬币与dp[i-2]的和是否大于dp[i-1]
		if(dp[i-2]+c[i-1]>dp[i-1])
		{
			dp[i]=dp[i-2]+c[i-1];
			if(s[j]==i-1) j--;//注意s[j]被赋值后值还可能会变,当满足这种情况时,
			//s[j]会被再次赋值,即新的值覆盖原来的值 
			s[++j]=i;//记录选取硬币的序号,j自增 
		}	
		else
		{
			dp[i]=dp[i-1];//这种情况不需要记录新的硬币序号,因为此时dp[i]选取的硬币情况和dp[i-1]一样 
		}
	}
	return dp[--i];//i最后跳出循环时多加了一次1,故还要减一 
} 
int main()
{
	int coins[N]={5,1,2,10,6,2};//初始化硬币数组 
	int s[N/2+1]={0};//该数组用来存储选择的硬币序号。因为最多选择N/2+1项,所以数组初始大小为N/2+1 
	int max,i;//max用来存放最大的硬币总金额 
	max=selectcoins(coins,N,s);//调用selectcoins函数,max为函数的返回值 
	printf("max=%d\n",max);
	printf("选取的硬币编号为:");//硬币编号从1开始 
	for(i=0;i<=j;i++)//输出硬币编号,注意此处是i<=j,而不是i
		printf("%d ",s[i]);
	return 0; 
}

最大子段和(书上解法)

//最大子段和问题(动态规划算法)
#include
#include
int MaxSum(int n,int *a){
	int sum=0,b=0;
	int i; 
	//若数组中只有1个数,则这个数就是最大子段和,将a[0]赋给b,然后判断b是否大于0确定sum 
	//如果数组中有2个数:
		//如果经过第一轮的赋值,b<0(即a[0]的值小于0),则将a[2]赋值给b,重复第一轮循环的操作 
		//如果b>0,b=b+a[1],并进行判断新b是否大于0,如果大于0则将b的值暂时赋值给sum
		//注意这里没有判断a[1]是否大于0,因为循环进行计算的是a[1:1]、a[1:2]...a[1:n]的最大子段和计算
		//如果b<0则不会走到最后 
	for(i=1;i<=n;i++){
		if(b>0)
		  b+=a[i];
		else
		  b=a[i];
		if(b>sum)
		  sum=b;
	}
	return sum;
}
int main(){
	int a[]={1,3,4,-5,4,-6,9,-7,5,4};
	int n=10;
	printf("最大子段和为:%d",MaxSum(n,a));
} 

最长公共子序列(书上解法)

//最长公共子序列问题
#include
#include
void LCSLength(int m,int n,char *x,char *y,int **c,int **b){
	int i,j;
	for(i=1;i<=m;i++)
		c[i][j]=0;
	for(i=1;i<=n;i++)
		c[0][i]=0;
	for(i=1;i<=m;i++){
		for(j=1;j<=n;j++){
			if(x[i]==y[j]){
				c[i][j]=c[i-1][j-1]+1;
				b[i][j]=1;
			}
			else if(c[i-1][j]>=c[i][j-1]){
				c[i][j]=c[i][j-1];
				b[i][j]=2;
			}
			else{
				c[i][j]=c[i][j-1];
				b[i][j]=3;
			}
		}
	}
}
void LCS(int i,int j,int *x,int **b){
	if(i==0||j==0)
		return;
	if(b[i][j]==1){
		LCS(i-1,j-1,x,b);
		printf("%c",x[i]);
	}
	else if(b[i][j]==2)
		LCS(i-1,j,x,b);
	else
		LCS(i,j-1,x,b);
}
int main(){
	int a[]={a,d,f,g,r};
	int b[]={w,r,g,r,f,a,d};
	int i=5,j=7;
	LCS(5,7,)	
}

最长公共子序列

#include 
#include 
#define MAXSIZE 10

int c[MAXSIZE][MAXSIZE];
int flag[MAXSIZE][MAXSIZE];
int a[MAXSIZE] = {1,2,3,2,4,1,2,5};
int b[MAXSIZE] = {2,4,3,1,2,1,5};
//递归找最长公共子序列长度
int find_longest(int asize,int bsize)
{
    int onenum,twonum;
    if(asize==0 || bsize == 0)
    {
        return 0;
    }
    else
    {
        if(a[asize-1]==b[bsize-1])
        {
            flag[asize][bsize]=1;
            c[asize][bsize] = c[asize-1][bsize-1]+1;
            return 1+find_longest(asize-1,bsize-1);
        }
        else
        {
            onenum = find_longest(asize-1,bsize);
            twonum = find_longest(asize,bsize-1);
            if(onenum>twonum)
            {
                c[asize][bsize] = onenum;
                flag[asize][bsize] = 2;
                return onenum;
            }
            else
            {
                c[asize][bsize] = twonum;
                flag[asize][bsize] = 3;
                return twonum;
            }
        }
    }
}
//动态规划寻找最长公共子序列长度
void dynamic_longest(int asize,int bsize)
{
    int i,j;
    for(i=1;i<=asize;i++)
    {
        for(j=1;j<=bsize;j++)
        {
            if(a[i-1]==b[j-1])
            {
                c[i][j] = c[i-1][j-1]+1;
                flag[i][j]=1;
            }
            else if(c[i-1][j]>=c[i][j-1])
            {
                c[i][j] = c[i-1][j];
                flag[i][j] = 2;
            }
            else
            {
                c[i][j] = c[i][j-1];
                flag[i][j]=3;
            }
        }
    }
}
void find_more(int i,int j)
{
    if(i==0 || j==0)
    {
        return ;
    }
    if(flag[i][j] ==1)
    {
        find_more(i-1,j-1);
        printf("%d",a[i-1]);
    }
    else if(flag[i][j]==2)
    {
        find_more(i-1,j);
    }
    else
    {
        find_more(i,j-1);
    }
}
int main()
{

    int i,j;
    int count=0;
    for(i=0;i<=MAXSIZE;i++)
    {
        for(j=0;j<=MAXSIZE;j++)
        {
            c[i][j] = 0;
            flag[i][j] = 0;
        }
    }
    //count = find_longest(8,7);
    //printf("count=%d\n",count);
    dynamic_longest(8,7);
    printf("count = %d\n",c[8][7]);
    printf("--------------------------\n");
    for(i=0;i<=MAXSIZE;i++)
    {
        for(j=0;j<=MAXSIZE;j++)
        {
            printf("%5d",c[i][j]);
        }
        printf("\n");
    }
    printf("----------------\n");
    for(i=0;i<=MAXSIZE;i++)
    {
        for(j=0;j<=MAXSIZE;j++)
        {
            printf("%5d",flag[i][j]);
        }
        printf("\n");
    }
    find_more(8,7);
}

最长公共子序列(另解)

//解决LCS问题同样是动态规划思想。LCS问题的结果同时受到两个字符串的影响,所以状态需要用二维表示。
//我们以f[i][j]表示a中从1到i、b中从1到j范围内的最长公共子序列的长度,那么考虑对于状态f[i][j]可以由哪些状态转移过来,
//在每一阶段我们能做出的决策有以下几种:
//若a[i]=b[j],则a[i]、b[j]同时属于公共子序列:
//f[i][j]由f[i-1][j-1]转移而来,f[i][j]=f[i-1][j-1]+1;
//若a[i]!=b[j],则a[i]、b[j]不能同时属于公共子序列:
//①a[i]不属于公共子序列:f[i][j]由f[i-1][j]转移而来,f[i][j]=f[i-1][j];
//②b[j]不属于公共子序列:f[i][j]由f[i][j-1]转移而来,f[i][j]=f[i][j-1];
//③a[i]、b[j]都不属于公共子序列:f[i][j]=f[i-1][j-1],这种决策可以舍去,
//因为只要f[i][j]发生了更新,都是由f[i-1][j-1]+1得到,其一定>f[i-1][j-1];
//该情形下舍去最后一项决策后,f[i][j]=max(f[i-1][j], f[i][j-1]);
int Lcs(int a[], int b[]) {
	int f[7][10]={0};
	int i=0,j=0;
	for(i=1;i<=6;++i) {
	for(j=1;j<=9;++j) {
		if(a[i-1]==b[j-1])
			f[i][j]=f[i-1][j-1]+1;//满足第3中情况,f[i][j]=f[i-1][j-1]+1 
		else//其他情况,进行判断 
			f[i][j]=Max(f[i-1][j], f[i][j-1]);
		}
	}
	return f[6][9];
}
int Max(int a,int b){
	if(a>b)
		return a;
	else
		return b;
} 
int main(){
	int a[]={1,3,4,5,8,7};
	int b[]={3,4,7,2,3,1,5,8,7};
	printf("最长公共子序列为:%d",Lcs(a,b));
}

第五次作业

电路布线

#include
#include
#define N 10
int main()
{
	int i,j,n;
	int a[N],dp[N][N];//a[i]表示上端点i对应的下端点坐标,dp数组用来储存上端点到下端点的最大不相交子集
	printf("请输入端点个数:");
	scanf("%d",&n);
	printf("请输入各端点对应的下端点:");
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);//依次存入上端点对应的下端点 
	} 
	for(i=1;i<=n;i++){//上端点为1的情况 
		if(i<a[1]) dp[1][i]=0;
		else dp[1][i]=1;
	}
	for(i=2;i<=n;i++){//其他情况 
		for(j=1;j<=n;j++){
			if(j<a[i]){//小于対应线的坐标 
				dp[i][j]=dp[i-1][j];
			}else{//大于等于 
				if(dp[i-1][j]>dp[i-1][a[i]-1]+1){//更新dp最大值 
					dp[i][j]=dp[i-1][j];
				}else
					dp[i][j]=dp[i-1][a[i]-1]+1;
			}
		}
	}
	printf("%d\n",dp[n][n]);
	return 0;
 } 

电路布线(另解)

#include 
#include 

void mnset(int *c, int **size, int n)
{
    int i, j;
    //i=1且j
    for (j=0;j<c[1];j++)
        size[1][j] = 0;
    // i=1且j>=c[1]时,最大不相交子集为1 
    for(j=c[1];j<=n;j++)
        size[1][j]=1;
    for(i=2;i<n;i++)//一般情况 
    {
        for(j=0;j<c[i];j++)
            size[i][j]=size[i-1][j];//j
        for(j=c[i];j<=n;j++)
            size[i][j]=(size[i-1][j]>size[i-1][c[i]-1]+1)?size[i-1][j]:(size[i-1][c[i]-1]+1);
		//前者为不选该线,后者为选用该线 
    }
    size[n][n]=(size[n-1][n]>size[n-1][c[n]-1]+1)?size[n-1][n]:size[n-1][c[n]-1]+1;
}

int traceback(int *c, int **size, int *net, int n)//回溯追踪 
{//net数组存储mnset中的m条连线 
    int i,j,m=0;
    j=n;
    for(i=n;i>1;i--)
    {
        if(size[i][j]!=size[i-1][j])//表示没选择当前线 
        {
            net[m++]=i;
            j=c[i]-1;
        }
    }
    if(j>=c[1])
        net[m++]=1;
    return m;
}

void print(int *c,int **size,int *net,int n,int m)
{
	printf("S[i,j]数组为:\n");
    	int i,j;
    	for (i=1;i<=n;i++)
    	{
      for(j=0;j<=n;j++)
            printf("%d ",size[i][j]);
      printf("\n");
   	}
   	printf("\n");
//输出上端线路编号
    	printf("上端线路编号:");
    	for(i=0;i<=n;i++)
        	printf("%d ",i);
    	printf("\n");
//输出下端线路编号
    	printf("下端线路编号:");
    	for(i=0;i<=n;i++)
        	printf("%d ",c[i]);
   	printf("\n");
//输出最大不相交子集的个数
    	printf("最大不相交子集的大小为:%d\n", size[n][n]);
//输出最大不相交子集中的各个导线
	printf("\n"); 
    	printf("上端线路编号:");
    	for(i=m-1;i>=0;i--)
        	printf("%d ",net[i]);
      printf("\n");
    	printf("下端线路编号:");
    	for(i=m-1;i>=0;i--)
        printf("%d ",c[net[i]]);
    	printf("\n");
}

int main(){

    	int n=10,m;
	int *c=(int*)malloc(sizeof(int)*(n+1));
	c[0]=0;c[1]=8;c[2]=7;c[3]=4;c[4]=2;c[5]=5;c[6]=1;c[7]=9;c[8]=3;c[9]=10;c[10]=6;
    	int **size=(int**)malloc(sizeof(int*)*(n+1));
    	int *net=(int*)malloc(sizeof(int)*(n+1));
//对c[]进行赋初值
	
//    c[1] = rand() % n + 1;
//    int i = 2;
//    while (i <= n)
//    {
//        int f = 0;
//        int t = rand() % n + 1;
//        int j=0; 
//        for (j = 1; j < i; j++)
//        {
//            if (c[j] == t)
//            {
//                f = 1;
//                break;
//            }
//        }
//        if (f == 0)
//        {
//            c[i] = t;
//            i++;
//        }
//    }
	int i;
    	for(i=1;i<=n;i++)
        	size[i]=(int*)malloc(sizeof(int)*(n+1));
    	mnset(c,size,n);
  	m=traceback(c,size,net,n);
    	print(c,size,net,n,m);
    	for(i=1;i<=n;i++)
      	free(size[i]);
    	free(c);
    	free(size);
    	free(net);
    	return 0;
}

0-1背包

#include
#include
#include
int max(int x,int y){
	int retValue=x>y?x:y;
	return retValue;
}
void knapsack(int n,int c,int *w,int *v,int **m){
	int i,j;
	//当背包重量为0时,c[i][0]=0; 
	for(i=0;i<=n;i++)
		m[i][0]=0; 
	//边界条件:只有第n个物体,背包容量分别为1,2,…,c的时候m的值
	for(j=1;j<=c;j++)
	    	if(j>=w[n])
	    		m[n][j]=v[n];
	    	else 
			m[n][j]=0;
	//依次求解m[i][j],1<=i
	for(i=n-1;i>=1;i--) 
		for(j=1;j<=c;j++)
			if(j<w[i])
				m[i][j]=m[i+1][j];
			else
				m[i][j]=(m[i+1][j]>(m[i+1][j-w[i]]+v[i]))?m[i+1][j]:(m[i+1][j-w[i]]+v[i]);
	printf("最大价值为:%d\n",m[1][c]);
}

//具体选了哪些物品 
void traceback(int **m ,int *w, int c, int n, int *x){//回溯求选用的物品 
	//求x[i],从m[1][c]开始
	int j=c,i;
	for(i=1;i<n;i++){
	    if(m[i][j]==m[i+1][j])//没有物品增加 
	        x[i]=0;
	    else{
	        x[i]=1;
	    	j=j-w[i];
		}
	}
	if (m[i][j]>0)
	   	x[n]=1;
	else               
	   	x[n]=0;
}
int main(){
	int n;//商品总数 
	int c;//背包总容量
	printf("商品总数:");
	scnaf("%d",&n);
	printf("\n"); 
	printf("背包总容量:");
	scanf("%d",&c);
	int *w=(int*)malloc(sizeof(int)*(n+1));//动态分配weights
	int *v=(int*)malloc(sizeof(int)*(c+1));//动态分配values
	int *x=(int*)malloc(sizeof(int)*(n+1));//动态分配所选择的商品 
	int i;
	for(i=1;i<=n;i++)
		scanf("%d",&w[i]);//start with index 1
	for(i=1;i<=n;i++)
		scanf("%d",&v[i]);//start with index 1
	w[0]=0;v[0]=0;
	int **m=(int**)malloc(sizeof(int*)*(n+1));//m[i][j] means j capacity with i...n goods to choose
	for(i=0;i<n+1;i++){
		m[i]=(int*)malloc(sizeof(int)*(c+1));
	}
	knapsack(n,c,w,v,m);
	traceback(m,w,c,n,x);
	printf("goods chosed:");
	for(i=1;i<=n;i++)
		if(x[i]==1)
			printf("%d ",i); 
}


0-1背包(另解)

#include"stdio.h"
#include"stdlib.h"
#include"string.h"
void knapsack(int n,int c,int *w,int *v,int **m){
	int i,j;
	//当背包重量为0时,c[i][0]=0; 
	for(i=0;i<=n;i++)
		m[i][0]=0; 
	//边界条件:只有第n个物体,背包容量分别为1,2,…,c的时候m的值
	for(j=1;j<=c;j++)
	    	if(j>=w[n])
	    		m[n][j]=v[n];
	    	else 
			m[n][j]=0;
	//依次求解m[i][j],1<=i
	for(i=n-1;i>=1;i--) 
		for(j=1;j<=c;j++)
			if(j<w[i])//剩余容量j小于当前物品重量w[i],装不下 
				m[i][j]=m[i+1][j];
			else//否则进行价值比较 
				m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
	printf("最大价值为:%d\n",m[1][c]);
}

int max(int x,int y){//这里不使用C自带的三目运算符,使用了就会发生错误(不知道为什么) 
	int bigger=x>y?x:y;
	return bigger;
}
//具体选了哪些物品 
void traceback(int **m ,int *w, int c, int n, int *x){
	//求x[i],从m[1][c]开始
	int j=c,i;
	for(i=1;i<n;i++){
	    	if(m[i][j]==m[i+1][j])//没有物品增加 
	        	x[i]=0;//记x[i]=0
	    	else{//选择该物品
	        	x[i]=1;//记x[i]=1
	    		j=j-w[i];//从背包剩余容量j中减去当前选择物品i的重量w[i]
		}
	}
	if (m[i][j]>0)
	   	x[n]=1;
	else               
	   	x[n]=0;
}
int main(){
	int n;//nums of goods 
	printf("物品数量:");
	scanf("%d",&n);
	int c;//capacity
	printf("背包容量:");
	scanf("%d",&c);
	int *w=(int*)malloc(sizeof(int)*(n+1));//weights
	int *v=(int*)malloc(sizeof(int)*(c+1));//values
	int *x=(int*)malloc(sizeof(int)*(n+1));//goods chosed
	int i;
	printf("依次输入物品重量:"); 
	for(i=1;i<=n;i++)
		scanf("%d",&w[i]);//start with index 1
	printf("依次输入物品价值:"); 
	for(i=1;i<=n;i++)
		scanf("%d",&v[i]);//start with index 1
	w[0]=0;v[0]=0;//0号商品的重量和价值都为0,作为边界条件 
	int **m=(int**)malloc(sizeof(int*)*(n+1));//m[i][j]意味着剩余容量j,当前选择待物品为i...n
	for(i=0;i<n+1;i++){
		m[i]=(int*)malloc(sizeof(int)*(c+1));
	}
	knapsack(n,c,w,v,m);
	traceback(m,w,c,n,x);
	printf("选择物品:");
	for(i=1;i<=n;i++)
		if(x[i]==1)
			printf("%d ",i); 
}


电路布线(优化)

#include 

int max(int a,int b){
	return a>b?a:b;
}
void MNS(int a[],int set[][11],int n){
    int i,j;
    for (i = 0; i < n; i++){			//保证不会将 a 数组中的第一个元素纳入到最长公共子序列中
        set[i][0] = 0;
        set[0][i] = 0;
    }
    for (i = 1; i <= n; i++){
        for (j = 1; j <= n; j++){
            if (a[i] != j)
                set[i][j] = max(set[i-1][j],set[i][j-1]);
            else
                set[i][j] = set[i-1][j-1] + 1;
        }
    }
}

void Traceback(int i,int j,int set[][11]){
    if (i == 0)
        return;
    if (set[i][j] == set[i-1][j])
        Traceback(i-1,j,set);
    else if (set[i][j] == set[i][j-1])
        Traceback(i,j-1,set);
    else{
        Traceback(i-1,j-1,set);
        printf("(%d,%d) ",i,j);
    }
}

int main(){
    int a[] = {0,8,7,4,2,5,1,9,3,10,6};
    int set[11][11];
    MNS(a,set,10);
    printf("最大连线数量为: %d \n",set[10][10]);
    printf("所选连线如下:");
    Traceback(10,10,set);
    return 0;
}

0-1背包问题(优化)

#include 
#include 

int m[10][10];
int x[10];

void Traceback(int [],int ,int );
void knapsack(int [],int [],int ,int );

void knapsack(int v[],int w[],int c,int n){
	int i,j,jMax;
	jMax=w[n]-1<c?w[n]-1:c;
	for(j=0;j<=jMax;j++)
		m[n][j]=0;
	for(j=w[n];j<=c;j++)
		m[n][j]=v[n];
	for(i=n-1;i>1;i--){
		jMax=(w[i]-1)<c?(w[i]-1):c;
		for(j=0;j<=jMax;j++)
			m[i][j]=m[i+1][j];
		for(j=w[i];j<=c;j++)
			m[i][j]=m[i+1][j]>(m[i+1][j-w[i]]+v[i])?m[i+1][j]:(m[i+1][j-w[i]]+v[i]);
	}
	m[1][c]=m[2][c];
	if(c>=w[1])
		m[1][c]=m[1][c]>(m[2][c-w[1]]+v[1])?m[1][c]:(m[2][c-w[1]]+v[1]);
}

void Traceback(int w[],int c,int n){
	int i;
	for(i=1;i<n;i++){
		if(m[i][c]==m[i+1][c])
			x[i]=0;
		else{
			x[i]=1;
			c-=w[i];
		}
	}
	x[n]=m[n][c]?1:0;
	printf("向量x的值依次是:");
	for(i=1;i<=n;i++)
		printf("%d ",x[i]);
}

int main(){
	int n=5,C=10;
	int w[]={0,2,2,6,5,4};
	int v[]={0,6,3,5,4,6};
	knapsack(v,w,C,n);
	printf("此背包问题的最优值是%d\n",m[1][C]);
	Traceback(w,C,n);
	return 0;
}

长江游艇问题

//长江游艇问题
//租用游艇问题
//长江游艇俱乐部在长江上设置了n个游艇出租站1,2,...,n。 
//游客可在这些游艇出租站租用游艇,并在下游任何一个游艇出租站归还游艇。
//游艇出租站i到游艇出租站j之间的租金为r(i,j)。试设计一个算法,计算出游艇出租站1到游艇出租站n所需的最少租金
#include
#define n 6
//static int choice[n][n]={0};

void MinCost(int fee[n][n], int dist[n][n]){
    	int i,j,k;
//    int choose[n][n]={0};
    	for(i=0; i<n; i++)
      	for(j=0; j<n; j++)
            	dist[i][j] = fee[i][j];//初始化数组 
    	for(k=0; k<n; k++)//暴力算法 
      	for(i=0; i<n; i++)
            	for(j=0; j<n; j++)
                		if(dist[i][k]+dist[k][j] < dist[i][j])//{//如果找到i到j的最省路径:i到k再由k到j 
                    		dist[i][j] = dist[i][k]+dist[k][j];//则更行最省路径
//					choose[i][j]=1;//choice数组置1,表示选择该数组
//				}
//	return choose;
}

int main()
{
	int i=0,j=0;
    	int fee[n][n]= {
      {0,9,14,18,25,40},
      {3,0,6, 15,18,30},
      {15,7,0,10,12,15},
      {18,17,10,0,4,7},
      {29,18,10,4,0,3},
      {34,23,15,7,4,0}
    	};
    	printf("各出租站之间的到达租金为:\n");
    	for(i=0;i<n;i++)
		for(j=0;j<n;j++){
	    		printf("%3d ",fee[i][j]);
	    		if(j==n-1)
	    			printf("\n");
		}
	printf("\n");
//	int **a;
	int minCost[n][n];//构造存放最小租金的二维数组 
//	a=Mincost(fee, minCost,choice);
//	static int choice[n][n]={0};
//	for(i=0;i
//		for(j=0;j
//			printf("%d",a[i][j]); 
    	MinCost(fee, minCost); 
    	printf("从出租站1到出租站%d的最少花费为:%d元\n", n-1,minCost[0][5]);
    	return 0;
}

长江游艇(优化)

#include 

int dis[205][205]; // dis[i][j]: 从第i站到j站所需的租金

int main(){
	int n, temp;
	printf("请输入出租站的个数:");
	scanf("%d", &n);
	// 输入数据并保存
	printf("请依次给定租金:");
	for(int i = 1; i < n; i++){
    	for(int j = i+1; j <= n; j++){
        	scanf("%d", &temp);
        	dis[i][j] = temp;
    	}
	}

	// 计算出站1到站n所需的最少租金
	for(int j = 2; j <= n; j++){ // 1站到j站最少租金
    	for(int i = 2; i < j; i++){ // i表示第几行开始
        	if(dis[1][j] > dis[1][i] + dis[i][j])
            	dis[1][j] = dis[1][i] + dis[i][j];
    	}
	}	

printf("%d\n", dis[1][n]);

return 0;
}

第六次作业

最优服务次序问题

#include
using namespace std;

int Partition(int a[], int p, int r) {
//分区函数,将数组a在区间[p,r]内的元素按照标记x分为左右两部分
    //定义两个下标i和j,分别指向分区的开头和结尾后面的一格 
    int i=p,j=r+1;
    int x=a[p];    //选择区间第一个元素作为基准值
    int t;
    //当i和j没有相遇时循环
    while(1){
        //从左向右找到第一个大于等于x的元素
        while((a[++i])<x&&i<r);
        //从右向左找到第一个小于等于x的元素
        while(a[--j]>x);
        //注意这俩while只移动i和j,不做其他操作,所以没有循环体 

        //如果i>=j则说明已经扫描完了整个区间,跳出循环
        if (i>=j){
            break;
        }
        //交换a[i]和a[j],使得小于x的元素都在左侧,大于x的元素都在右侧
        t=a[i];
        a[i]=a[j];
        a[j]=t;
        //此时一次循环完成, 
    }
    //将枢轴x放到分区位置j上
    a[p]=a[j];
    a[j]=x;
    return j;
}

void QuickSort(int a[], int p, int r) {
//快速排序函数,递归实现
    int q;
    //如果区间长度为1或0,则已经有序,直接返回
    if (p<r) {
        //使用Partition函数将数组分为左右两部分
        q=Partition(a,p,r);
        //对左半部分[p,q-1]进行排序
        QuickSort(a,p,q-1);
        //对右半部分[q+1,r]进行排序
        QuickSort(a,q+1,r);
    }
}

double greedy(int x[],int n){
//贪心算法,以等待时间较短者优先
//输出最小平均等待时间 
	QuickSort(x,0,n-1);
	int i;
	for(i=1;i<n;i++)	//计算每个顾客的等待时间,新的x数组为{1,1+12,1+12+33...} 
		x[i]+=x[i-1];
	double t=0;
	for(i=0;i<n;i++)
		t+=x[i];
	t/=n;				//计算最小平均等待时间 
	return t;
}

int main(){
    int t[]={56,12,1,99,1000,234,33,55,99,812};		//10名顾客的等待时间 
    int n=sizeof(t)/sizeof(t[0]);
	printf("平均等待时间为%f",greedy(t,n));
    return 0;
}

删数问题

#include
#include
using namespace std;

void delek(char a[], int k){
    int m=strlen(a);
    int i,j;
    if(k>=m){
        a[0]='\0';
        return;
    }
    while(k>0){
        int i;
        for(i=0;i<strlen(a)-1 && (a[i]<=a[i+1]);i++);
        //找到第一个i,使得a[i]>a[i+1]
        for(j=i;j<strlen(a)-1;j++){
        //把这个i删掉(即从i到最后往前挪一位) 
            a[j]=a[j+1];
        }
        a[strlen(a)-1]='\0'; //将字符串最后一位设为'\0',以便输出结果 
        k--;
    }
    while(strlen(a)>1 && a[0]=='0'){
    //删除前导零
        for(int j=0;j<strlen(a)-1;j++){
            a[j]= a[j+1];
        }
        a[strlen(a)-1]='\0'; //将字符串最后一位设为'\0',以便输出结果 
    }
}

int main(){
    char a[]="178543";		 //以字符串的形式给定原数字 
    printf("删数前的数字为:%s\n",a);
    printf("输入要删掉的位数:");
    int k=0;
    scanf("%d",&k);
//  int k=4;				 //要删掉几位 
//  printf("Before: %s\n", a);
    delek(a,k);
    printf("删掉%d位后的数字为: %s\n",k,a);
    return 0;
}

活动安排问题

#include
#include
void GreedySelector(int n,int s[],int f[],bool A[]){//活动结束时间按非减排序 
//共有n个活动,s[i]为第1个活动开始时间,f[i]为第i个活动结束时间,A[]用于记录是否选择该活动 
	A[1]=true;//选择第1个活动,每次选择最早结束的活动 
	int i,j=1;
	for(i=2;i<=n;i++){
		if(s[i]>=f[j]){//若活动i开始时间s[i]晚于活动j结束时间f[j] 
			A[i]=true;//选择活动A[i] 
			j=i;//并将j置为i 
		}
	else
		A[i]=false;//表示不选择活动A[i],继续寻找无时间冲突的活动 
	} 
}
int main(){
	int n=11,k=0;
	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};
	bool A[11]={};
	for(k=0;k<11;k++){
		printf("活动%d的开始时间为%d,结束时间为%d\n",k+1,s[k],f[k]); 	
	}
	GreedySelector(11,s,f,A);
	printf("\n贪心算法选择的活动为:");
	for(k=0;k<11;k++)
		if(A[k]!=0)
			printf("%d ",k);
}

背包问题

#include
#include
void Knapsack(int n,float M,float v[],float w[],float x[]){//贪心算法解决背包问题(非0-1背包) 
//	Sort(n,v,w);//Sort函数,排序各个物体的单位体积下的价值(价值v/质量w) 
	int i;
	for(i=1;i<=n;i++)//n为所有物品的总大小,用x[i]=0表示没被装入背包,x[i]=1表示被装入背包了 
		x[i]=0;
	float c=M;//c被赋初值为原始背包大小 
	for(i=1;i<=n;i++){
		if(w[i]>c)//背包满了 
			break;
		x[i]=1;//表示这个单位体积的物品被全部装入背包 
		c-=w[i];//现在背包的大小c要被减去装入物品占据的大小 
	}
	if(i<=n)
		x[i]=c/w[i];//x[i]表示第i个物品的所占体积在当前规定背包最大容量M的条件下被装入的比例
				//比如x[i]=0.5,表示第i个物品的一半被放入背包,另一半没有放入 
}
int main(int argc,char *argv[]){
	int n=3,i;
	float M=50;
	float v[]={0,60,100,120};
	float w[]={0,10,20,30};
	float x[3]={0};//x[i]用于存放装入背包的单件物品的多少 
	Knapsack(n,M,v,w,x);
	printf("装入背包的物品为:");
	for(i=1;i<=n;i++)
		printf("%f  ",x[i]);
	return 0; 
}

最短服务次序问题

#include
#include
//应使用短作业优先(需要服务时间短的顾客先服务)的方法
double greedy(int x[]){
	int n=x.size();
	sort(x.begin(),x.end());
	for(int i-1;i<n;i++)
		x[i]+=x[i-1];
	double t=0;
	for(i=0;i<n;i++)
		t+=x[i];
	t/=n;
	return t;
} 
int main(){
	int x[]={56,12,1,99,1000,234,33,55,99,812};
	printf("平均最短服务时间为%d",greedy(x));
}


第七次作业

N皇后问题

#include
#include
//n皇后问题 
int n;
int x[20];
int place(int k){
	int i;
	for(i=1;i<k;i++)
		if(abs(k-i)==abs(x[k]-x[i]) || x[k]==x[i])
			return 0;
	return 1;
} 
int queen(){
	int i;
	x[1]=0;
	int t=1;
	int sum=0;
	while(t>0)
	{
		x[t]+=1;
		while(x[t]<=n && !place(t))
			x[t]++;
		if(x[t]<=n)
			if(t==n){
				sum++;
				for(i=1;i<=n;i++) printf("%d ",x[i]);
				printf("\n");
			}
			else
				x[++t]=0;
			else
				t--;
	}
	return sum;
}
int main(int argc,char *argv[]){
	int t;
	printf("请输入要放置的皇后的个数:");
	scanf("%d",&n);
	t=queen();
	printf("共有%d个解\n",t);
	return 0;
}

回溯法 0-1背包

#include
#include

int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值 value
double w[100];//各个物品的重量 weight
double cw=0.0;//当前背包重量 current weight
double cp=0.0;//当前背包中物品总价值 current price
double bestp=0.0;//当前最优价值 best price
double perp[100];//单位物品价值(排序后) per price
int order[100];//物品编号
int put[100];//设置是否装入,为1的时候表示选择该组数据装入,为0的时候表示不装入
//按单位价值排序
void knapsack(){
	int i,j;
	int temporder=0;
	double temp=0.0;
	for(i=1;i<=n;i++)
		perp[i]=v[i]/w[i];//计算单位价值(单位重量的物品价值) 
	for(i=1;i<=n-1;i++)
	{
		for(j=i+1;j<=n;j++)
			if(perp[i]<perp[j])//冒泡排序prep[],order[],sortv[],sortw[]
			{
				temp=perp[i];//冒泡对prep[]排序
				perp[i]=perp[i]; 
				perp[j]=temp;
				temporder=order[i];//冒泡对order[]排序
				order[i]=order[j]; 
				order[j]=temporder;
				temp=v[i];//冒泡对v[]排序
				v[i]=v[j];
				v[j]=temp;
				temp=w[i];//冒泡对w[]排序
				w[i]=w[j];
				w[j]=temp; 
			}
	}
} 
//回溯函数
void backtrack(int i){//i用来指示到达的层数(第几步,从0开始),同时也指示当前选择完了几层
	double bound(int i);
	if(i>n){//递归结束的判定条件 
		bestp=cp;
		return; 
	} 
	//如若左子结点可行,则直接搜素左子树
	//对于右子树,先计算上界函数,以判断是否将其减去
	if(cw+w[i]<=c){//将物品i放入背包,搜索左子树
		cw+=w[i];//同步更新当前背包的重量
		cp+=v[i];//同步更新当前背包的总价值
		put[i]=1;
		backtrack(i+1);//深度搜索进入下一层
		cw-=w[i];//回溯复原 
		cp-=v[i];//回溯复原 
	}
	if(bound(i+1)>bestp)//如若符合条件则搜索右子树
		backtrack(i+1); 
} 
//计算上界函数,功能为剪枝
double bound(int i){//判断当前背包的总价值cp+剩余容量可容纳的最大价值<=当前最优价值 
	double leftw=c-cw;//剩余背包容量
	double b=cp;//记录当前背包的总价值cp,最后求上界
	//以物品单位重量价值递减次序装入物品
	while(i<=n && w[i]<=leftw){
		leftw-=w[i];
		b+=v[i];
		i++;
	} 
	//装满背包
	if(i<=n)
		b+=v[i]/w[i]*leftw;
	return b;//返回计算出的上界 
} 
int main(int argc,char *argv[]){
	int i;
	printf("请输入物品的数量和背包的容量:");
	scanf("%d %lf",&n,&c);
	printf("请依次输入%d个物品的重量:\n",n);
	for(i=1;i<=n;i++){
		scanf("%lf",&w[i]);
		order[i]=i;
	}
	printf("请依次输入%d个物品的价值:\n",n);
	for(i=1;i<=n;i++){
		scanf("%lf",&v[i]);
	}
	knapsack();
	backtrack(1);
	printf("最优价值为:%lf\n",bestp);
	printf("需要装入的物品编号是:");
	for(i=1;i<n;i++){
		if(put[i]==1)
			printf("%d ",order[i]);
	}
	return 0;
} 

回溯 最优装载

//装载问题
#include
#include

int n;//集装箱数
int cw;//当前载重量,current weight
int bestw;//最优载重重量
int r;//剩余集装箱重量
int c1;//第一艘轮船的载重量
int c2;//第二艘轮船的载重量
int x[100];//当前解
int bestx[100];//当前最优解
int w[100];//集装箱重量数组
void BackTrack(int i)
{
	if(i>n)
	{
		if(cw>bestw)
		{
			for(i=1;i<=n;++i)
				bestx[i]=x[i];
			bestw=cw;
		}
		return;
	}
	r-=w[i];
	if(cw+w[i]<=c1)//约束条件
	{
		cw+=w[i];
		x[i]=1;
		BackTrack(i+1);
		x[i]=0;
		cw-=w[i]; 
	}
	if(cw+r>bestw)//限界函数
	{
		x[i]=0;
		BackTrack(i+1); 
	} 
	r+=w[i];
} 
int main(int argc,char *argv[]){
	int i;
	int restweight=0;
	printf("请输入货物的个数n:");
	scanf("%d",&n);
	printf("请分别输入两艘轮船的载重量c1和c2:");
	scanf("%d %d",&c1,&c2);
	printf("请依次输入每件货物的重量:");
	for(i=1;i<=n;i++)
		scanf("%d",&w[i]);
	bestw=0;
	r=0;
	cw=0;
	for(i=1;i<=n;i++)
		r+=w[i];
	BackTrack(1);
	for(i=1;i<=n;i++)
		if(bestx[i]==0)
			restweight+=w[i];
	if(restweight>c2)
		printf("不能装入\n");
	else
	{
		printf("船1装入的货物为:");
		for(i=1;i<=n;i++)
			if(bestx[i]==1)
				printf(" %d",i);
		printf("\n船2装入的货物为:");
		for(i=1;i<=n;i++)
			if(bestx[i]!=1)
				printf(" %d",i);
	}
	return 0;
} 
 
 

连续邮资问题

#include
#define maxl 1000    //表示最大连续值 
#define maxint 32767
int n,m;            //n为邮票种类数,m为能贴的最大张数 
int maxvalue;       //表示最大连续值
int bestx[100];     //表示最优解
int y[maxl];        //y[k],存储表示到k值,所使用的最少邮票数 
int x[100];         //存储当前解 
void backtrace(int i,int r);
 
int main(){
	printf("请输入邮票面值数:");
 	scanf("%d",&n);
 	printf("请输入能张贴邮票的最大张数:");
 	scanf("%d",&m);
 	for(int i=0;i<=n;i++){
  		x[i]=0;
  		bestx[i]=0;
 	} 
 	for(int i=0;i<maxl;i++){
  		y[i]=maxint;
 	}
 	x[1]=1;
 	y[0]=0;
 	maxvalue=0;
 	backtrace(1,0);
	printf("当前最优解为:");
 	for(int i=1;i<=n;i++){
  		printf("%d ",bestx[i]);
 	} 
 	printf("\n最大连续邮资为:");
 	printf("%d",maxvalue);
 	return 1;
} 
 
void backtrace(int i,int r){
 	for(int j=0;j<=x[i-1]*m;j++){    	   //对上一层的邮资值数组进行更新,上限是x[i-1]*m 
  	if(y[j]<m){
   		for(int k=1;k<=m-y[j];k++){      //从只使用一个x[i]到使用m-y[i]个,即使用最多的最大值,降低邮票数 
    			if(y[j]+k<y[j+x[i]*k]){
     				y[j+x[i]*k]=y[j]+k;   
//如果前面的某一个情况加上k个x[i],所达到邮资值使用的邮票数少于原来的邮票数则更新 
    				}
   			}
  		}
 	}
 	while(y[r]<maxint){                    //向后寻找最大邮资值 
  		r++;
 	}
 	if(i==n){                              //i=n表示到达叶子节点。 
  		if(r-1>maxvalue){                //若大于最大值,则更新最优值与最优解 
   			for(int k=1;k<=n;k++){
          			bestx[k]=x[k];    
   			}
   			maxvalue = r-1;
  		}
  	return;
 	}
 	int z[maxl];
 	for(int k=0;k<maxl;k++){               //由于每一层需要对多种情况进行运算,因此需要将上一层的邮资值数组保留 
  		z[k] = y[k];
 	}
 	for(int j=x[i]+1;j<=r;j++){            //对下一层进行运算 
  		x[i+1]=j;
  		backtrace(i+1,r-1);
  		for(int k=0;k<maxl;k++)
   			y[k]=z[k];
 		}
}

你可能感兴趣的:(数据结构与算法,算法,c++,数据结构)