算法导论实验——背包问题和凸多边形最优三角剖分问题

大家如果对算法导论以及其他信息感兴趣,可以加入学习交流群一起交流 948097478

1背包问题
有五个物品,其重量分别是{2,2,6,5,4},价值分别为{6,3,5,4,6},背包的容量为10.问如何分配可以使得背包中的物体价值最高?

问题分析:
1.确定背包问题可用动态规划方法求解,即确定背包问题具有最优子结构的性质:
设(x1,x2,…,xn)是所给0/1背包问题的一个最优解,则(x2,…,xn)是下面一个问题的最优解:
在这里插入图片描述
如若不然,设(y2,…,yn)是上述子问题的一个最优解,则:
在这里插入图片描述
因此:
在这里插入图片描述
这说明了(x1,y2,…,yn)是所给0/1背包问题比(x1,x2,…,xn)更优解,从而导致矛盾,因此可说明背包问题具有最优子结构的性质,可以用动态规划方法求解。

2.子问题最优值得递归关系

令V(i,j)表示前i(1<=i<=n)个物品中能够装入容量为j(1<=j<=C)的背包中的物品的最大值。
对于第i个物品是否装入背包,分为以下两种情况:
(1)背包容量不足以装入物品i,则xi=0,背包不增加价值;
(2)背包容量可以装下物品i,则xi=1,背包的价值增加了vi。
据此,可得到V(i,j)的递归公式:

V(i,0)=V(0,j)=0
在这里插入图片描述

3.根据递归公式求最优代价——建表

填表过程:
第一阶段,只装入前一个物品,确定在各种情况下的背包能够得到的最大价值;
第二阶段,只装入前2个物品,确定在各种情况下的背包能够得到的最大价值;以此类推,知道第n个阶段。
最后,V(n,C)是容量为C的背包中装入n个物品时取得的最大价值。

4.根据V表求解最优解序列
为了确定装入背包的具体物品,从V(n,C)的值向前推,如果V(n,C)>V(n-1,C),表明第n个物品被装入背包,前n-1个物品被装入容量为C-wn的背包中;否则,第n个物品没有被装入背包,前n-1个物品被装入容量为C的背包中。以此类推,直到确定第一个物品是否被装入背包中为止。由此,得到如下函数:
在这里插入图片描述
源代码

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <windows.h>
using namespace std;

int n=5,C=10;//物品的个数和背包的容量
int w[6] = {0,2,2,6,5,4};//物品的重量
int v[6] = {0,6,3,5,4,6};//物品的价值
int V[6][11];//存放能够存放物品的最大价值
int x[6];//记录背包中是否存放物品,0为无,1为有

int KnapSack(int n, int w[], int v[]){
	int i, j;
	for (i = 0; i <= n; i++) V[i][0] = 0;//初始化数组
	for (j = 0; j <= C; j++) V[0][j] = 0;//初始化数组
	for (i = 1; i <= n;i++) 
		for (int j = 1; j <= C;j++)
		if (j < w[i]) V[i][j] = V[i - 1][j];
		else
			V[i][j] = max(V[i - 1][j], V[i - 1][j - w[i]] + v[i]);//找到最优解		j = C;
		for (i = n; i>0; i--){
			if (V[i][j] > V[i - 1][j]){
				x[i] = 1; j = j - w[i];//背包容量可以装下物品i,xi=1
			}
			else x[i] = 0;//背包容量不足以装入物品i,xi=0
		}
		return V[n][C];
}

int main(void){	
	V[6][C]=KnapSack(6, w, v);
	printf("背包最大价值:%d\n", V[6][C]);
	printf("背包中有以下物品:\n");
	for (int i = 1; i < 6; i++){
		if (x[i] == 1) {
			printf("第%d个物品,重量:%d,价值:%d\n", i,w[i-1],v[i-1]);
		}	
	}
	return 0;
}

运行结果
在这里插入图片描述

2.凸多边形最优三角剖分问题:
用多边形顶点的逆时针序列表示凸多边形,即P={v0,v1,…,vn-1}表示具有n条边的凸多边形。
在这里插入图片描述

给定凸多边形P,以及定义在由多边形的边和弦组成的三角形上的权函数w。要求确定该凸多边形的三角剖分,使得该三角剖分中诸三角形上权之和为最小。

问题分析:

若凸(n+1)边形P={v0,v1,…,vn-1}的最优三角剖分T包含三角形v0vkvn,1≤k≤n-1,则T的权为3个部分权的和:三角形v0vkvn的权,子多边形{v0,v1,…,vk}和{vk,vk+1,…,vn}的权之和。可以知道,由T所确定的这2个子多边形的三角剖分也是最优的。因为若有{v0,v1,…,vk}或{vk,vk+1,…,vn}的更小权的三角剖分将导致T不是最优三角剖分的矛盾。

在这里插入图片描述
定义一个t[i][j],1<=i<=j<=N,为凸子多边形{vi-1,vi,…,vj}的最优三角剖分所对应的权函数值,即其最优值。据此定义,要计算的凸(n+1)边形P的最优权值为t[1][n]。
t[i][j]的值可以利用最优子结构性质递归地计算。当j-i≥1时,凸子多边形至少有3个顶点。由最优子结构性质,t[i][j]的值应为t[i][k]的值加上t[k+1][j]的值,再加上三角形vi-1vkvj的权值,其中i≤k≤j-1。由于在计算时还不知道k的确切位置,而k的所有可能位置只有j-i个,因此可以在这j-i个位置中选出使t[i][j]值达到最小的位置。由此,t[i][j]可递归地定义为:

当i=j时,t[i][j]=0;
当i<j时,t[i][j]=min{t[i][k]+t[k+1][j]+w(vi-1* vk* vj)};

对于要求的t[1][n],可以用通过由下至上的,从链长(多边形的边)为2开始计算,每次求t[i][j]的最小值,并且记录最小值所对应的K值,根据最优子结构的性质,逐步向上就可以求出t[1][n]的最小值。

源代码

#include <stdio.h>

#define N 6        //顶点数/边数

int weight[][N] = { { 0, 2, 2, 3, 1, 4 }, { 2, 0, 1, 5, 2, 3 }, { 2, 1, 0, 2, 1, 4 }, { 3, 5, 2, 0, 6, 2 },
{ 1, 2, 1, 6, 0, 1 }, { 4, 3, 4, 2, 1, 0 } };

int t[N][N];    //t[i][j]表示多边形{Vi-1VkVj}的最优权值
int s[N][N];    //s[i][j]记录Vi-1到Vj最优三角剖分的中间点K

int get_weight(const int a, const int b, const int c)
{
	return weight[a][b] + weight[b][c] + weight[c][a];
}

void minest_weight_val()
{
	int i, r, k, j;
	int min;

	for (i = 1; i < N; i++)
	{
		t[i][i] = 0;
	}

	for (r = 2; r < N; r++)
	{
		for (i = 1; i < N - r + 1; i++)
		{
			j = i + r - 1;
			min = 9999;        //假设最小值
			for (k = i; k < j; k++)
			{
				t[i][j] = t[i][k] + t[k + 1][j] + get_weight(i - 1, k, j);
				if (t[i][j] < min)        //判断是否是最小值
				{
					min = t[i][j];
					s[i][j] = k;
				}
			}
			t[i][j] = min;        //取得多边形{Vi-1,Vj}的划分三角最小权值
		}
	}
}

void back_track(int a, int b)
{
	if (a == b) return;
	back_track(a, s[a][b]);
	back_track(s[a][b] + 1, b);    
	printf("最优三角: V%d V%d V%d.\n", a - 1, s[a][b], b);
}



int main()
{
	minest_weight_val();
	printf("result:%d\n", t[1][N - 1]);
	back_track(1, 5);
	return 0;
}

运行结果
在这里插入图片描述

有兴趣可以加群一起讨论:948097478
在这里插入图片描述

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值