您的位置:首页 > 其它

dp学习之BadNeighbors解法

2016-04-04 11:53 543 查看
这是来自Topcoder的一道动态规划有关的题目,原文如下

The old song declares “Go ahead and hate your neighbor”, and the residents of Onetinville have taken those words to heart. Every resident hates his next-door neighbors on both sides. Nobody is willing to live farther away from the town’s well than his neighbors, so the town has been arranged in a big circle around the well. Unfortunately, the town’s well is in disrepair and needs to be restored. You have been hired to collect donations for the Save Our Well fund.

Each of the town’s residents is willing to donate a certain amount, as specified in the int[] donations, which is listed in clockwise order around the well. However, nobody is willing to contribute to a fund to which his neighbor has also contributed. Next-door neighbors are always listed consecutively in donations, except that the first and last entries in donations are also for next-door neighbors. You must calculate and return the maximum amount of donations that can be collected.

题目大意就是:有一组首尾相连的数组,要求在不选取相邻的数据的前提下,挑选其中的数据使其和最大。

如:{10,3, 2, 5, 7, 8}

结果:19

分析:这组数据有如下可选取的可能10+2+7或3+5+8显然10+2+7更大(注意:因为是首尾相连所以第一个元素和最后一个也是相邻数据)

{11, 15}

结果:15

问题分析求解:

假设数据为x0,x2,…..,xn-1,并且这组数据首尾无关(为降低问题复杂度),用数组dp
存储在计算过程中包含xi的子问题的最大值。

由于使用动态规划的两要素是:最优子结构和子问题重叠,所以先来刻画一个最优子结构。很容易知道当i=0时,dp[0] = x0, 当i>0时,由于不能选取相邻元素所以i - 1不在考虑范围,所以有两种选择一种选dp[j](其中j < i - 1)中最大的和xi相加,还有就是只要xi,因为j < i-1,所以不会有相邻元素包含在dp[i]中因此必然符合条件。

状态转移方程:

dp[i] = x0; 其中( i = 0 )

dp[i] = max(xi, dp[j] + xi)其中(j < i - 1);

最大值为val = max(dp[0],….,dp
);

但上述求解过程是在数据首尾不相连的情况下进行的,可题目条件是首尾相连。此时我们可以将原问题分解为两个子问题分别求解,然后选取他们解的最大值。假设数据为a0,…,an,则可将问题分解为a0,….,an-1(要首不要尾),a1,…..an(要尾不要首)

程序源码(c++):

#include <iostream>
#include <vector>
using namespace std;
int max(int a, int b);
class BadNeighbors
{
public:
int solve(int a, int b, vector<int> &arr);
int maxDonations(vector<int> donations);
};

int main(void)
{
BadNeighbors badNb;
//  int arr[] = {10, 3, 2, 5, 7, 8};
int arr[] = {7, 7, 7, 7, 7, 7, 7};
int max;
vector<int> vec(arr, arr + sizeof(arr) / sizeof(int));
max = badNb.maxDonations(vec);
cout << max << endl;
return 0;
}

int BadNeighbors::solve(int a, int b, vector<int> &arr)
{
int dp[b - a + 2];
int ans = 0;
for(int i = a; i < b + 1; i++)
{
if(i == a)
{
dp[i] = arr[a];
ans = arr[a];
}
else
{
dp[i] = arr[i];
for(int j = a; j < i - 1; j++)
{
dp[i] = max(dp[i], dp[j] + arr[i]);
}
if(dp[i] > ans)
ans = dp[i];
}
}
return ans;
}

int BadNeighbors::maxDonations(vector<int> donations)
{
int n = donations.size();
int a = solve(1, n - 1, donations);
int b = solve(0, n - 2, donations);
if(a > b)
return a;
else
return b;
}

int max(int a, int b)
{
if(a > b)
return a;
else
return b;
}


参考文章原文来自http://apps.topcoder.com/forums//?module=Thread&threadID=757580&start=0&mc=10重点内容

Hi. I think you’ve already solved this problem, but this will help whoever is learining dp. (as me)

Let’s supose we have an array ‘donations’ which is the amount of money form the person i

Let’s think a little bit and define the following

Let dp[i] be the maximum amount of money USING THE PERSON i and using zero or more persons less than i-1

It follows that

dp[0] = donations[0] (base case)

Then

dp[i] = max( donations[i] , dp[j] + donations[i] for every j
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  动态规划