随笔 - 68  文章 - 57  trackbacks - 0
<2012年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用链接

留言簿(8)

随笔分类(74)

随笔档案(68)

搜索

  •  

最新评论

阅读排行榜

评论排行榜

  给定n个数求这n个数划分成互不相交的m段的最大m子段和。
  经典的动态规划优化的问题。设f(i, j)表示前i个数划分成j段,且包括第i个数的最大m子段和,那么有dp方程:
    f(i, j) = max { f(i - 1, j) + v[i], max {f(k, j - 1) + v[i]}(k = j - 1 ... i - 1) }
  也就是说第i个数要么自己划到第j段,要么和前一个数一起划到第j段里面,转移是O(n)的,总复杂度O(n * n * m)。
  可以引入一个辅助数组来优化转移。设g(i, j)表示前i个数划分成j段的最大子段和(注意第i个数未必在j段里面),那么递推关系如下:
    g(i, j) = max{g(i - 1, j), f(i, j)},分是否加入第i个数来转移
  这样f的递推关系就变成:
    f(i, j) = max{f(i - 1, j), g(i - 1, j - 1)} + v[i],转移变成了O(1)
  这样最后的结果就是g[n][m],通过引入辅助数组巧妙的优化了转移。实现的时候可以用一维数组,速度很快。

附HDU 1024题目代码:
#include <cstdio>
#include 
<algorithm>
using namespace std;
const int N = 1000010, INF = 0x3fffffff;

int f[N], g[N], a[N];

int max_sum(int m, int n)
{
    
int i, j, t;
    
for (i = 1; i <= n; i++)
    {
        t 
= min(i, m);
        
for (j = 1; j <= t; j++)
        {
            f[j] 
= max(f[j], g[j-1]) + a[i];
            g[j
-1>?= f[j-1];
        }
        g[j
-1>?= f[j-1];
    }
    
return g[m];
}

int main()
{
    
int m, n;

    
while (scanf("%d %d"&m, &n) == 2 && m && n)
    {
        
for (int i = 1; i <= n; i++)
        {
            f[i] 
= g[i] = -INF;
            scanf(
"%d"&a[i]);
        }
        printf(
"%d\n", max_sum(m, n));
    }

    
return 0;
}
posted on 2009-06-19 11:18 sdfond 阅读(4832) 评论(4)  编辑 收藏 引用 所属分类: Algorithm - Dynamic Programming

FeedBack:
# re: 最大M子段和 2010-04-24 10:27 qq258513813
能不能给我更详细点呢? 动态规划感觉比较难理解,这方面没有什么基础,好急哦。  回复  更多评论
  
# re: 最大M子段和 2010-04-24 18:36 sdfond
@qq258513813
我分析过程写的很详细了,实现的时候降了一维,你要是知道背包问题如何实现的话这个就不难理解,如果实在理解不了,就先把最基础的那些学了吧,最大字段和、子矩阵、子立方体什么的,我感觉我已经不能更详细了-_-!  回复  更多评论
  
# re: 最大M子段和 2011-03-16 17:55 阿皮
学习了, 谢谢  回复  更多评论
  
# re: 最大M子段和[未登录] 2012-09-20 10:58 Bill
@阿皮
可以看这个链接http://blog.163.com/sentimental_man/blog/static/7300161820119109533172/
比作者的详细很多。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理