程序基本算法习题解析 动态规划-线段覆盖: 数轴上有n条线段,线段的两端都是整数坐标,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合),且线段价值之和最大。

题目:

线段覆盖:数轴上有n条线段,线段的两端都是整数坐标,坐标的范围为0~1000000,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合),且线段价值之和最大。输入第一行一个整数n(n<=1000),表示有多少条线段接下来的n行,每行3个整数ai,bi,ci,分别代表第i条线段的左端点ai,右端点bi,价值ci。输出能够获得的最大价值。

思路:

①首先定义一个结构体Line,存放各线段的左端点、右端点以及其价值;

②再将线段按右端点的升序进行排序;定义一个maxValue数组,maxValue[i]表示以线段 i 为右边界的线段最大价值。

③对于每一条线段 i,计算其最大价值的公式为:maxValue[i] = max{ maxValue[i-1] , line[i].v + maxValue[j] },其中线段 j 为线段 i 之前最靠近线段 i 且与线段 i 不重合的线段。line[i]是一个结构体,line[i].v表示线段 i 的价值。因此可以按照排序后的下标从小到大对线段进行遍历,求其最大价值。

看文字叙述大概率已经晕了,但是不慌,看下面的例子应该就理解了:

(6条线段,左端点、右端点、价值分别为 1 2 3;1 3 1;2 4 3;3 4 10;4 6 5;5 6 8)

按右端点的升序排序后如下:

首先,以第0条线段为右边界的线段最大价值大为第1条线段本身的价值,即maxValue[0] = 3;然后从第1条线段开始遍历,根据公式maxValue[i] = max{ maxValue[i-1] , line[i].v + maxValue[j] },第1条线段之前没有与其不重合的线段,因此不存在maxValue[j] 这一项,其最大价值取前一条线段的最大价值和它自身的价值中的最大值,即前一条线段的价值,即maxValue[1] = maxValue[0] = 3;再看第2条线段,第2条线段之前最靠近第2条线段且与第2条线段不重合的线段为线段0,即 j = 0;line[2].v + maxValue[0] = 3+3 = 6,而前一条线段的最大价值maxValue[1] = 3,取max有maxValue[2] = 6;再看第3条线段,第3条线段之前最靠近第3条线段且与第3条线段不重合的线段为线段1,即 j = 1;line[3].v + maxValue[1] = 10+3 = 13,而前一条线段的最大价值maxValue[2] = 6,取max有maxValue[3] = 13;...直到遍历完全部线段。

代码如下:

// Chapter14_1.cpp : Defines the entry point for the application.
// 线段覆盖
// 数轴上有n条线段,线段的两端都是整数坐标,坐标的范围为0~1000000,每条线段有一个价值,
// 请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合),且线段价值之和最大。
// 输入第一行一个整数n(n<=1000),表示有多少条线段
// 接下来的n行,每行3个整数ai,bi,ci,分别代表第i条线段的左端点ai,右端点bi,价值ci。
// 输出能够获得的最大价值
 
#include "stdafx.h"
#include<iostream>
#include<algorithm>
using namespace std;

struct Line
{
	int s; //线段的左端点
	int e; //线段的右端点
	int v; //线段的价值
}line[1000]; //定义1001条线段
int maxValue[1000]; //存放(按右端点升序排序后的)以每条线段为右边界的线段最大价值
//比较函数,sort函数的第三个参数
bool cmp(Line a,Line b)
{
	return a.e < b.e; //按照线段的右端点进行升序排序
}
int main()
{
	int n; //线段条数
	cout << "输入线段条数:";
	cin >> n;
	int i,j;
	//输入线段的左端点、右端点和价值
	for(i=0;i<n;i++)
	{
		cout << "输入第" << i << "条线段的左端点、右端点和价值:";
		cin >> line[i].s >> line[i].e >> line[i].v;
	}
	//对线段进行排序(按照右端点升序排序)
	sort(line,line+n,cmp); 
	//以第0条线段为右边界的线段最大价值大为第0条线段本身的价值
	maxValue[0] = line[0].v;
	//从第1条线段开始遍历
	for(i=1;i<n;i++)
	{
		//先将以该条线段为右边界的线段最大价值赋为以前一条线段为右边界的线段最大价值
		maxValue[i] = maxValue[i-1];
		//遍历该条线段之前的线段(倒回去遍历)
		for(j=i-1;j>=0;j--)
		{
			//找出第一条与该条线段不重合的线段(某条右端点小于等于该条左端点的线段)
			if(line[j].e <= line[i].s)
			{
				//若前面第一条与该条线段不重合的线段的最大价值加该条线段的价值大于上一条线段的最大价值
				if((line[i].v + maxValue[j]) > maxValue[i-1])
					//以该条线段为右边界的线段最大价值赋为前面第一条与该条线段不重合的线段的最大价值加该条线段的价值
					maxValue[i] = line[i].v + maxValue[j];
				break; 
			}
		}
	}
	//数组maxValue中的每个值代表的就是以每条线段为右边界的线段最大价值
	for(i=0;i<n;i++)
	{
		cout << "以第" << i << "条线段为边界的线段最大价值为:" << maxValue[i] << endl;
	}
	//cout << "最大价值为:" << maxValue[n-1] << endl;
	system("pause");
	return 0;
}

为了让大家更清楚动态规划的过程,这里把以每条线段为右边界的线段最大价值都打印出来了,运行结果如下:

这里提一下sort函数的用法:

(需要包含头文件<algorithm>)

sort函数有三个参数:第一个是要排序的数组的起始地址,第二个是结束的地址,第三个参数是排序的方法(默认是从小到大)。

例如有一个结构体node,

struct node
{
	int a;
	int b;
	int c;
};

定义了一个node类型的数组 node arr[100],想对它进行排序:先按a值的升序排列,如果a值相同,再按b值的降序排列,如果b还相同,就按c降序排列。

#include<algorithm>
...

bool cmp(node x,node y)
{
	if(x.a != y.a)
		return x.a < y.a;
	if(x.b != y.b)
		return x.b > y.b;
	return x.c > y.c;
}

'''
sort(arr,arr+100,cmp);

书上也有程序,在此附上书上程序供大家学习:

// Chapter14_1.cpp : Defines the entry point for the application.
// 线段覆盖
// 数轴上有n条线段,线段的两端都是整数坐标,坐标的范围为0~1000000,每条线段有一个价值,
// 请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合),且线段价值之和最大。
// 输入第一行一个整数n(n<=1000),表示有多少条线段
// 接下来的n行,每行3个整数ai,bi,ci,分别代表第i条线段的左端点ai,右端点bi,价值ci。
// 输出能够获得的最大价值
#include "stdafx.h"
#include<iostream>
#include<algorithm>
using namespace std;

//书上程序
int n;
struct node
{
	int s,e,v;
}q[1001];
//int dp[1000];
int ans = 0;
//按e值升序排列
int cmp(const node x,const node y)
{
	return x.e < y.e;
}
int main()
{
	cin >> n;
	for(int k=0;k<n;k++)
		cin >> q[k].s >> q[k].e >> q[k].v;
	sort(q,q+n,cmp);
	ans = 0;
	for(int i=1;i<n;i++)
	{
		int temp = 0;
		for(int j=0;j<i;j++)
		{
			if(q[i].s >= q[j].e)
			{
				if(temp < q[j].v)
					temp = q[j].v;
			}
		}
		q[i].v = q[i].v + temp;
		if(ans < q[i].v)
			ans = q[i].v;
	}
	cout << ans << endl;
	system("pause");
	return 0;
}

运行结果如下: 

猜你喜欢

转载自blog.csdn.net/elma_tww/article/details/86484738