计蒜客 The Heaviest Non-decreasing Subsequence Problem dp LIS变形 || 线段树+dp

16 篇文章 0 订阅

题目链接


题意:

每个数有一个val和weight,如果val < 0 weight 为 0 , val >= 1e4 weight 为5 ,其余为1.让你求一哥序列使得为weight最大并且val是不下降的.


思路:

我们知道在求普通的LIS的时候存的东西可能会被交换,换的时候会使得weight并不一定是最优解、

当时看大家过的那么多就写了一个坑爹的做法,把所有数离散一下用线段树维护了1 - id[i] 的最大值,维护一个最优解.

dp[i]表示LIS 第i个选上的最大值(即选出的这些使得weight最大的有第i个)。那么新加入的这个需要加在前面使得值最大的那个上.

(离散化之后下标对应值,所以一定是不下降的了。)

这个过程用线段树维护一下区间最大值即可.


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 3e5+5;
int a[maxn], v[maxn], val[maxn], Hash[maxn], id[maxn];
int dp[maxn], treeMax[maxn*4], Max;

void update(int root, int l, int r, int pos, int val)
{
    if(l == r)
    {
        treeMax[root] = max(treeMax[root], val);
        return ;
    }
    int mid = (l+r)/2;
    if(pos <= mid) update(root*2, l, mid, pos, val);
    else update(root*2+1, mid+1, r, pos, val);
    treeMax[root] = max(treeMax[root*2], treeMax[root*2+1]);
}

void query(int root, int l, int r, int i, int j)
{
    if(i <= l && j >= r)
    {
        Max = max(treeMax[root], Max);
        return ;
    }
    int mid = (l+r)/2;
    if(mid >= i) query(root*2, l, mid, i, j);
    if(mid < j) query(root*2+1, mid+1, r, i, j);
}

int main(void)
{
    int x, n = 1;
    while(~scanf("%d", &a[n]))
    {
        v[n] = a[n];
        if(a[n] >= 10000) v[n] -= 10000;
        if(a[n] >= 10000) val[n] = 5;
        else if(a[n] >= 0) val[n] = 1;
        else val[n] = 0;
        Hash[n] = v[n];
        n++;
    }
    n--;
    sort(Hash+1, Hash+1+n);
    int d = unique(Hash+1, Hash+1+n)-Hash-1;
    for(int i = 1; i <= n; i++)
        id[i] = lower_bound(Hash+1, Hash+1+d, v[i])-Hash;
    int ans = 0;
    for(int i = 1; i <= n; i++)
    {
        Max = 0;
        query(1, 1, n, 1, id[i]);
        dp[i] = Max+val[i];
        ans = max(ans, dp[i]);
        update(1, 1, n, id[i], dp[i]);
    }
    printf("%d\n", ans);
    return 0;
}


其实这个题仔细想一下就是把价值为0的去掉,然后把价值为5的分成5个一样的放进去,然后求一次LIS,得到的LIS长度就是最大值了.


PS:如果价值不是5而是很大的数这个方法就不可以了.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=5e5+10;

int s[maxn]; 
int dp[maxn];
int main(){

	int len = 0;
	int x;
	while(~scanf("%d",&x))
	{
		if(x >= 0 && x < 10000)
		{
			s[len++] = x;
		}
		if(x >= 10000)
		{
			 x -= 10000;
			for(int i = 1;i <= 5;++i)
			s[len++] = x;
		}
	}
	//cout<<len<<endl;
		int ta = 0;  
        for(int i = 0;i < len;i++)  
        {  
            if(ta == 0 || s[i] >= dp[ta])  
            dp[++ta] = s[i];  
            else  
            {  
                int tmp = upper_bound(dp+1,dp+1+ta,s[i]) - dp;  
                dp[tmp] = min(dp[tmp],s[i]);  
            }  
        }  
        cout<<ta<<endl;
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值