【DP经典问题】

最大连续和:

O(n) 算法: f[n] = max(f[n-1]+a[n], a[n])

O(nlogn) 算法:分治算法,算法竞赛入门经典有介绍

hackerrank给了一个 k-dane 算法:http://blog.csdn.net/joylnwang/article/details/6859677


最大子序列和(不要求连续):f[i]表示 1-i 最大子序列和,用d[i]表示最大子序列的结束位置。。


LIS问题:

nlogn算法: d[i] 保存长度为i的上升子序列的最后一个元素,维护d[i]使得d[i]总是最小。

可以这样理解,对于相同长度的IS,最后一个元素较小的子序列增长的“潜力”较大。

如果需要输出LIS,则 d[i] 保存的是下标,pa[i]保存上一个元素下标。代码见LCS问题。

// poj 2533
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#include<set>
#include<list>
using namespace std;

const int maxn = 1000000;
const int inf = 0x7fffffff;
int T, n;
int a[maxn+5];
int d[maxn+5];

int main() {
#if 0
    freopen("input.in", "r", stdin);
#endif
        scanf("%d",&n);
        if (n == 0) {printf("0");return 0;}
        for (int i=1;i<=n;++i) scanf("%d", &a[i]);
        for (int i=1;i<=n;++i) d[i] = inf;
        d[0] = 0;
        d[1] = a[1];
        for (int i=2;i<=n;++i) {
            int *p = lower_bound(d+1, d+n, a[i]);
            if (*p > a[i]) 
                *p = a[i];
        }
        int ans = 1;
        for (int i=n;i>=1;--i)
            if (d[i] != inf) {
                ans = i;
                break;
            }
        //for (int i=1;i<=n;++i) cout << d[i] << ' ';cout << endl;
        printf("%d\n", ans);
    return 0;
}


LCS问题:

nlogn算法:http://blog.csdn.net/non_cease/article/details/6918848

将LCS转换成LIS来解决,思想也非常容易理解。

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;

#define rep(i, s, t) for(int (i)=(s);(i)<=(t);++(i))
#define urep(i, s, t) for(int (i)=(s);(i)>=(t);--(i))

const int inf  = 0x7fffffff;
const int maxn = (int)(1e5);
int n, m, len;
int a[maxn+5];
int b[maxn+5];
int d[maxn+5];
int pa[maxn+5];
vector<int> sav[maxn+5];
vector<int> arr; // array to get LIS
map<int, int> ha;

bool cmp(int lhs, int rhs) {
    if (lhs == inf) return false;
    if (rhs == inf) return true;
    return arr[lhs] < arr[rhs];
}

int main() {
#if 1
    freopen("input.in", "r", stdin);
#endif
    cin >> n >> m;
    rep(i, 0, n-1) cin >> a[i];
    rep(i, 0, m-1) cin >> b[i];

    // map element in a [1-1e9] to [1-1e6], hash_value = position where the element firstly appearanced
    // hash_value = ha[origin_value], origin_value = a[hash_value]
    rep(i, 0, n-1) {
        if (ha.count(a[i]) == 0) {
            ha[a[i]] = i;
        }
    }
    // for every distinct element in a, find its appearance in b
    urep(i, m-1, 0) {
        if (ha.count(b[i])) {
            sav[ha[b[i]]].push_back(i);
        }
    }

    rep(i, 0, n-1) {
        if (ha.count(a[i])) {
            int id = ha[a[i]];
            int sz = sav[id].size();
            rep(j, 0, sz-1) arr.push_back(sav[id][j]);
        }
    }

    len = min(n, m);
    rep(i, 0, len) d[i] = inf;
    d[1] = 0;
    d[0] = -1;
    pa[0] = -1;
    int sz = arr.size();
    rep(k, 0, sz-2) cout << arr[k] << " ";cout << arr[sz-1] << endl;
    rep(k, 1, sz-1) {
        int *p = lower_bound(d+1, d+len, k, cmp);
        if (cmp(k, *p)) {
            pa[k] = *(p-1);
            *p = k;
        }
    }
    int idx;
    for(idx=len;idx>=1;--idx)
        if (d[idx] != inf) break;
    vector<int> ans;
    int cur = d[idx];
    //cout << "length: " << idx << endl;
    while (cur != -1) {
        ans.push_back(b[arr[cur]]);
        cur = pa[cur];
    }
    sz = ans.size();
    rep(i, 0, sz-2) cout << ans[sz-1-i] << " ";cout << ans[0] << endl;

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值