• 全部
  • 问答

Standford的一个算法作业题

azhen 2008-04-08 03:57:18
Maximum sum of increasing subsequence

Given an integer array, how will you find out the increasing subsequence which gives the largest sum. For example,
50,23,1,67,30 in this subsequence a few possible increasing subsequences are
1) 23,30
2) 23,67
2) 50,67
3) 1,67
4) 1,30
5) 67
6) 30

but50, 67 gives the maximum sum. How to find it?

我只能想到n^的算法,在网上搜索heaviest increasing string,有现成的解法,复杂度为nlgn,但没看明白.....
...全文
304 点赞 收藏 17
写回复
17 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
sammyhk 2008-05-01
楼主可以说说怎么得出50, 67这个答案的? 题目没怎么看明白.
回复
UltraBejing 2008-05-01
接分是王道!
回复
fluke 2008-04-16
用二分查找就是nlogn的了,不过二分在本质上也是树
回复
sun3411 2008-04-15
我发的有错, 不用看了...
还是要树..
回复
a3fd_34234jda1 2008-04-15
思想:设输入序列为a1,a2,a3...an, 程序在遍历中计算和最大的子串
初始a1生成第一个串,其后每个元素都按照如下方法计算:
1)如果后面的元素大于第一个串的最后元素,由于生成串的顺序是按照从大到小(不理解请看下面的例子), 所以程序只需保留所有串中和最大的串
2)如果小于第一个串,则从第二个串起开始比较,如果大于第N个串,则加入从第N个串开始的所有串,只需保留从第N个串中和最大的串。
3)如果该元素小于所有串的最后一个元素,则单独生成一个串。

思想的关键是“串i的最后一个加入的元素大于串i+1,串i+2,... 串n“

举例:50,23,1,67,30
1.初始
Sum
串1:50 50

2.读取23,由于23<50,单独生成一个串
Sum
串1:50 50
串2:23 23

3.读取1,由于1<第一个串,且小于第二个串起的所有串(在此即23),单独生成一个串
Sum
串1:50 50
串2:23 23
串3:1 1

4.读取67,由于67大于第一个串(由于在判断的过程中保证了串1的最后一个元素必然大于其他串的最后一个入串
的元素),所以所有串都直接加上67,由于所有串都加入相同的元素,则可以知道此后所有串加入的元素必然相同,由于题目是求所有串中最大的元素,此时保留和最大的串即可。
Sum
串1:50,67 117
串2:23,67 90
串3:1, 67 68
由于串1的Sum最大,只需保留串1,所以结果:
Sum
串1:50,67 117
读取下一个字符,跟每个串的最后一个元素进行比较........

由于在读取数据中进行有效的裁减,程序复杂度远远小于O(n^2).思想的关键是“串i的最后一个加入的元素大于串i+1,串i+2,... 串n“

回复
sun3411 2008-04-14
1. data[i]存输入的数据
2. seqdata[i]存排序后的数据
3. dataindex[i]存data[i]在seqdata[]中索引
4. seq dataindex[i]存seqdata[i]在data[i]中索引
5. seqnextindex[]和seqprevindex,辅助数组,与seqdata[]组成链表形势的数据结构(懒得去用链表^_^)
6. beforeindex[i]是data[i]前面比data[i]小的数中最大的那个数的索引
7. afterindex[i]是data[i]后面比data[i]大的数中最小的那个数的索引
最后要用的是 data[], beforeindex[], afterindex[].
方法还是动态规划,
设ret[i]为data[0]到data[i-1]中产生的最大和,且链中最大数小于data[i]//就是说data[i]也能放进去
对第i个数做如下处理:
1.当ret[i]未被初始化时, 先求ret[i]
a.beforeindex[i]为-1, 则ret[i] = 0.
b.beforeindex[i]不为-1,则ret[i] = ret[beforeindex[i]]+data[beforeindex[i]].
2.设ret[afterindex[i]]的值.
ret[afterindex[i]] 为 ret[i]+data[i]与ret[afterindex[i]]中的大的一个
回复
sun3411 2008-04-14
代码有点乱, 嘻嘻

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define MAX_COUNT 1000

int data[MAX_COUNT];
int seqdata[MAX_COUNT];
int dataindex[MAX_COUNT];
int seqdataindex[MAX_COUNT];
int seqnextindex[MAX_COUNT];
int seqprevindex[MAX_COUNT];
int beforeindex[MAX_COUNT];
int afterindex[MAX_COUNT];
int maxret[MAX_COUNT];
int count = 10;
char *files[]={"test.in","test.input","test.sort","test.relate","test.ret"};
void display_to_file(char *file)
{
int i;
FILE *pf;
if(file == 0)return;
pf = fopen(file,"w");
fprintf(pf," => data ind seqdata ind >ind <ind ret<=\n");
for(i=0;i<count;i++)
{
fprintf(pf,"%3d %10d %3d %10d %3d %3d %3d %10d\n",
i,data[i],dataindex[i],seqdata[i],seqdataindex[i],
afterindex[i],beforeindex[i],maxret[i]);
}
fclose(pf);
}
void input(char* file) /*data*/
{
int i;
if(file == 0)
{
srand(time(0));
for(i=0;i<count;i++)
{
data[i] = rand() % 10000;
seqdata[i] = data[i];
dataindex[i] = i;
seqdataindex[i] = i;
}
}
else
{
FILE *pf = fopen(file,"r");
if(pf == 0)
{
count = 10;
input(0);
}
fscanf(pf,"%d",&count);
for(i=0;i<count;i++)
{
fscanf(pf,"%d",&data[i]);
seqdata[i] = data[i];
dataindex[i] = i;
seqdataindex[i] = i;
}
fclose(pf);
}
display_to_file("test.input");
}

int qstestdummyf[MAX_COUNT];
int qstestdummyi[MAX_COUNT];
void qstest(int left, int right)
{
int mid = (left+right)/2;
int l,r,i;
if(left >= right) return;
if(left == right - 1)
{
if(seqdata[left]>seqdata[right])
{
int tmpf = seqdata[left];
int tmpi = seqdataindex[left];
seqdata[left] = seqdata[right];
seqdataindex[left] = seqdataindex[right];
seqdata[right] = tmpf;
seqdataindex[right] = tmpi;
}
return;
}
qstest(left,mid);
qstest(mid+1,right);
l = left;
r = mid+1;
for(i=left;i<=right;i++)
{
if(r<=right && (l>mid || seqdata[l]>seqdata[r]) )
{
qstestdummyf[i] = seqdata[r];
qstestdummyi[i] = seqdataindex[r];
r++;
}
else
{
qstestdummyf[i] = seqdata[l];
qstestdummyi[i] = seqdataindex[l];
l++;
}
}
for(i=left;i<=right;i++)
{
seqdata[i] = qstestdummyf[i];
seqdataindex[i] = qstestdummyi[i];
}
}
void getsortinfo()/*seqdata dataindex seqdataindex*/
{
int i;
qstest(0,count-1);
for(i=0;i<count;i++)
{
dataindex[ seqdataindex[i] ] = i;
}
display_to_file("test.sort");
}
void initseqindex()/*seqnextindex seqprevindex*/
{
int i;
for(i=0;i<count;i++)
{
seqnextindex[i] = i+1;
seqprevindex[i] = i-1;
}
}
void getrelatedinfo()/*beforeindex afterindex*/
{
int i,index;
initseqindex();
for(i=0;i<count;i++)
{
index = dataindex[i];
if(seqnextindex[index]>=0 && seqnextindex[index]<count)
afterindex[i] = seqdataindex[ seqnextindex[index] ];
else afterindex[i] = seqnextindex[index];
seqnextindex[ seqprevindex[index] ] = seqnextindex[index];
seqprevindex[ seqnextindex[index] ] = seqprevindex[index];
}
initseqindex();
for(i=count-1;i>=0;i--)
{
index = dataindex[i];
if(seqprevindex[index]>=0 && seqprevindex[index]<count)
beforeindex[i] = seqdataindex[ seqprevindex[index] ];
else beforeindex[i] = seqprevindex[index];
seqnextindex[ seqprevindex[index] ] = seqnextindex[index];
seqprevindex[ seqnextindex[index] ] = seqprevindex[index];
}
display_to_file("test.relate");
}
int getanswerflag[MAX_COUNT];
int getanswer()/*maxret*/
{
int ret = 0;
int i;
for(i=0;i<count;i++)
getanswerflag[i] = 0;
for(i=0;i<count;i++)
{
if(getanswerflag[i] == 0)
{
getanswerflag[i] = 1;
if(beforeindex[i] == -1)
maxret[i] = 0;
else maxret[i] = maxret[beforeindex[i]]+data[beforeindex[i]];
}
if(afterindex[i] == count)
{
if(ret<maxret[i]+data[i])ret=maxret[i]+data[i];
}
else
{
if(getanswerflag[afterindex[i]]==0)
{
maxret[afterindex[i]]=maxret[i]+data[i];
getanswerflag[afterindex[i]]=1;
}
else if(maxret[afterindex[i]]<maxret[i]+data[i])
maxret[afterindex[i]]=maxret[i]+data[i];
}
/*
{
char buff[]="??test.ret";
buff[0] = i/10 + '0';
buff[1] = i%10 + '0';
display_to_file(buff);
}
*/
}
display_to_file("test.ret");
return ret;
}
/*
data[0]....data[n] input data

using sort to get follow O(n(log n))
seqdata[0]....seqdata[n] sorted data
dataindex / seqdataindex
seqdata[ dataindex[i] ] = data[ i ]
data [seqdataindex[i] ] = seqdata[ i ]

seqnextindex / seqprevindex help to get follow O(n)

beforeindex / afterindex
data[ beforeindex[i] ] = max{data[0]..data[i-1]}<data[i]
data[ afterindex[i] ] = min{data[i+1]..data[n]}>data[i]

to get result O(n)
maxret[i] max result before data[i] and data[i] can be add in
*/

int main(int argc, char* argv[])
{
int ret;
if(argc == 1)
input("test.in");
else if(argc == 2)
{
sscanf(argv[1],"%d",&count);
input(0);
}
getsortinfo();
getrelatedinfo();
ret = getanswer();
printf("result: %d\n",ret);
return 0;
}
回复
azhen 2008-04-13
The following is an alternative solution that uses augmented red-black trees. If
you’re comfortable with counting the deletion time separate from insertion time
for the solution above, feel free to skip it. If you fear amortization like the plague,
read on.
So, for each key, we will wish to compute the heaviest subsequence
ending in that key. We will keep an augmented red-black tree of these
(key,weight of subsequence) pairs keyed on the key. We will refer to the weight
of the subsequence as ws, not to be confused with the weight of the element.
We will also keep pointers for each element of which element is its predecessor
in the heaviest subsequence ending in that element. At the end, we will search
through the tree for the highest weight and follow the pointers to find our heaviest
subsequence.
For each element i, we will need to find in the tree the node x with the highest
weight such that key[x] < key[i]. x is the node that we should append i onto to
get the heaviest increasing subsequence ending in i. To do this, we will augment
each node with W[x], the maximum weight of subsequences ending in elements
less than or equal to x in the subtree rooted at x. We can efficiently calculate W[x]
as max(W[left[x]],ws[x]). Since this property only depends on the values stored
at node x and its children, we can maintain it during insertion, deletion, and
rotation. We will also keep at each node a pointer P[x] which points to the node
which attains that maximum weight. P[x] = P[left[x]] if W[left[x]] > weight[x]
and P[x] = x if weight[x] W[left[x]].
When we look at a new element i , we will insert it into the red-black tree. We
initialize totalweight = −1 and pointer = NIL. We’ll search in the tree for
where i should go. Everytime we follow a left child, we do nothing. Everytime we
follow a right child of some parent x, we set totalweight = max(totalweight,W[x])
and pointer = P[x]ifW[x] > totalweight. So, we essentially keep track of the
maximum weight and the pointer to the node that had it for all keys that are less
than i. When we find where to insert x, we set the weight of the subsequence
ending in x to be ws[x] = totalweight+weight[x]. We also set W[x] = ws[x] and
P[x] = x. In addition, in our original array, we add a pointer from x to pointer,
telling us how to backtrack to find the heaviest increasing subsequence.
We do this for all elements in the sequence (starting with the dummy element
−1 with weight 0). Each insert into the tree takes time lg n and we have to
perform n of them. At the end, we search through the tree for the highest weight
((n)) and follow the pointers back from that element to find the actual heaviest
increasing sequence (O(n)). So, the total running time is (n lg n).
回复
qq06161120 2008-04-09
求最大子序列和,好像不用树吧
import java.util.*;

public class test1003 {

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int start = 0;
int endnum = 0;
int num, maxnum;
int i;
num = Integer.parseInt(sc.next());
int t = 0;
int n, m;
int k;
int j;
int max[] = new int[num];
int start1[] = new int[num];
int end[] = new int[num];
for (i = 0; i < num; i++) {
n = sc.nextInt();
int a[] = new int[n];

for (j = 0; j < n; j++)
a[j] = sc.nextInt();

maxnum =-9999;

int thisSum = 0;
for (m = 0, j = 0; j < a.length; j++) {
thisSum += a[j];
if (thisSum > maxnum) {
maxnum = thisSum;
start = m;
endnum = j;
} else if (thisSum < 0) {

m = j + 1;
thisSum = 0;
}
}

max[t] = maxnum;


start1[t] = start;
end[t] = endnum;
t++;
}
for (j = 0; j < num; j++) {
k = j + 1;
if (j > 0)
System.out.println("");
System.out.println("Case " + k + ":");
System.out.println(max[j]+" "+(start1[j] + 1)+" "
+(end[j] + 1));

}

}

}
回复
tailzhou 2008-04-09
想错了;

每个关键字最多插入一次,最多删除一次;
插入删除的总的复杂度不超过o(nlogn)

这样是可以保证整个的复杂度为o(nlogn);
回复
tailzhou 2008-04-09
ls的 不一定是o(nlogn)

比如:

假设之前状态集合S为m[1]....m[n];
现有x[1]>x[i], ,1<i<=n;
那么之后的状态集合为S为m[1];

如何删除m[2]...m[n]且保证该过程的复杂度为0(logn)?
回复
动态规划。记这n个数组成的序列为x[1..n],
构建数组m[i],1≤i≤n,表示以x[i]结尾的递增子序列的最大和,则问题的解为 max{m[i],1≤i≤n},
递推关系为:m[i]=x[i]+max{0, m[k] | x[k]<x[i], 1<=k<i}
上述算法时间复杂度为O(n^2)。


上述算法可进一步进行优化
1、若x[i]<x[j],m[i]=m[j],则m[j]这个状态不必保留。
2、若x[i]<x[j],m[i]>m[j],则m[j]这个状态不必保留。
综合上述两点,我们得出了状态m[k]需要保留的必要条件:不存在i使得:x[i]<x[k]且m[i]≥m[k]。
于是,我们保留的状态中不存在相同的状态值,且随着状态值的增加,最后一个元素的值也是单调递增的。
也就是说,设当前保留的状态集合为S,则S具有以下性质:
对于任意i∈S, j∈S, i≠j有:m[i]≠m[j],且若m[i]<m[j],则x[i]<x[j],否则x[i]>x[j]。
可以发现,S实际上是以x值为关键字(也是以m值为关键字)的有序集合。使用平衡树实现有序集合S,则算法的时间复杂度可以降到O(n*logn)
回复
mincomp 2008-04-08
哦?这个怎么用后缀树做
回复
medie2005 2008-04-08
suffix tree.
回复
mincomp 2008-04-08
ls说的是平衡树(treap?)或者线段树吧
回复
medie2005 2008-04-08
那个解法是基于后缀树的吧。
回复
oo 2008-04-08
能否把解法贴出来?
回复
相关推荐
发帖
数据结构与算法
创建于2007-08-27

3.2w+

社区成员

数据结构与算法相关内容讨论专区
申请成为版主
帖子事件
创建了帖子
2008-04-08 03:57
社区公告
暂无公告