其实这个问题原题是这样描述的:
- 有N个节点,每两个节点相邻,每个节点只与2个节点相邻,因此,N个顶点有N-1条边。每一条边上都有权值wi,定义节点i到节点i+1的边为wi。
求:不相邻的权值和最大的边的集合。
对于这个问题可能看起来不是很好处理,把问题更加规范化一些:给出一个数组,求出其中一个子集,使得子集中每个元素在原数组中两两都不相邻并使子集的和最大。
因为不能选择两个相邻的元素,那么对于第i个元素的选择的可能性就包含选择i和不选择i个元素,至于选与不选其实是和第i-1个元素有直接关系的。
考虑两种情况:
1> 选择i,那么第i-1个元素一定不能选
2> 不选择i,那么第i-1个元素是可以选,也可以不选的,这决定于第i-2个元素对i-1的影响。
从这两种情况中可以看出,如果知道第i-1次被选中和不被选中时前i个元素(元素下标从0开始计算)了最大子集和,那么我们可以算出选择i和不选择i各可以获得的最大子集和。
那么我们可以得到一个这样的公式, 其中DS[i]记录第i个元素被选中时的前i+1个元素的最大子集和。 NS[i]记录第i个元素未被选中时的前i+1个元素的最大子集和
DS[i] = NS[i-1]+ data[i];
NS[i] = max(NS[i-1], DS[i-1])
接下来看一下例子,假设现在有一个8个元素的数组:
1 7 4 0 9 4 8 8
那么 NS数组为:
0 1 7 7 7 16 16 24
DS数组为
1 7 5 7 16 11 24 24
下面是程序的代码
#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;
#define NMax 1000
int data[NMax];
int table[NMax][2];
int GetMaxSubsetSum(int len)
{
memset(table, 0 , sizeof(table));
//第0行表示NS,表示该元素未被选中
//第一行表示DS,表示该元素被选中
table[0][0] = 0;
table[0][1] = data[0];
//动态规划 过程
for (int i = 1; i < len; ++i)
{
table[i][0] = max(table[i-1][1], table[i-1][0]);
table[i][1] = table[i-1][0] + data[i];
}
//打印原始数组
for (int i = 0; i < len; ++i)
cout << data[i] <<"\t";
cout <<endl;
//打印NS数组
for (int i = 0; i < len; ++i)
cout << table[i][0] <<"\t";
cout <<endl;
//打印DS数组
for (int i = 0; i < len; ++i)
cout << table[i][1] <<"\t";
cout <<endl;
//返回整个数组的最大值
return max(table[len-1][0], table[len-1][1]);
}
int main()
{
int len; cin >> len;
if (len <= 0)return 1;
for (int i = 0; i < len ; ++i)
{
data[i] = rand()% 10;
}
cout << GetMaxSubsetSum(len)<<endl;
}