CodeForces 28 D.Don't fear, DravDe is kind(dp+map)

256 篇文章 0 订阅
202 篇文章 1 订阅

Description

给出 n n 辆卡车,编号1~ n n ,现在要从这些卡车中选出若干组成车队,以编号从小到大排,对于第i辆卡车,其人数为 ci c i ,价值为 vi v i ,要求其前面卡车总人数为 li l i ,后面卡车总人数 ri r i ,问满足这些条件的车队总价值最大值

Input

第一行一整数 n n 表示车数量,之后n行每行四个整数 vi,ci,li,ri(1n105,1vi104,1ci105,0li,ri105) v i , c i , l i , r i ( 1 ≤ n ≤ 10 5 , 1 ≤ v i ≤ 10 4 , 1 ≤ c i ≤ 10 5 , 0 ≤ l i , r i ≤ 10 5 )

Output

输出使得总价值最大的卡车数量和编号

Sample Input

5
1 1 0 3
1 1 1 2
1 1 2 1
1 1 3 0
2 1 3 0

Sample Output

4
1 2 3 5

Solution

对于被选的车,其前面车的总人数,后面车总人数加上这辆车的人数即所选车总人数,必然为定值,故把所有 li+ci+ri l i + c i + r i 值相同的车拿出来单独考虑,这样在转移过程中就不用考虑 ri r i 的限制了,以 dp[i] d p [ i ] 表示总人数为 i i 且所选的车满足条件时的最大价值,对于第i辆车,如果存在 j j 满足lj=li+ci,那么有转移 dp[lj]=max(dp[lj],dp[li]+vi) d p [ l j ] = m a x ( d p [ l j ] , d p [ l i ] + v i ) ,只有 li=0 l i = 0 的车可以用来更新初值,只有 ri=0 r i = 0 的车可以用来更新答案,用 map m a p dp d p 数组便于查询满足条件的后继

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=100005;
int n,pre[maxn],s[maxn];
map<int,P>m;
P ans;
struct node
{
    int v,c,l,r,id,sum;
    bool operator<(const node &b)const
    {
        if(sum!=b.sum)return sum<b.sum;
        return id<b.id;
    }
}a[maxn];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",&a[i].v,&a[i].c,&a[i].l,&a[i].r);
        a[i].id=i;a[i].sum=a[i].c+a[i].l+a[i].r;
    }
    sort(a+1,a+n+1);
    int res=0;
    s[++res]=1;
    ans.first=0;
    for(int i=2;i<=n;i++)
    {
        if(a[i].sum==a[i-1].sum)s[++res]=i;
        if(a[i].sum!=a[i-1].sum||i==n)
        {
            m.clear();
            for(int j=1;j<=res;j++)
            {
                node t=a[s[j]];
                if(t.l==0)
                {
                    if(m.find(t.c)==m.end()||m[t.c].first<t.v)
                    {
                        m[t.c]=P(t.v,t.id);
                        pre[t.id]=0;
                        if(t.r==0&&m[t.c].first>ans.first)ans=m[t.c];
                    }
                }
                else
                {
                    if(m.find(t.l)!=m.end())
                    {
                        if(m.find(t.l+t.c)==m.end()||m[t.l+t.c].first<m[t.l].first+t.v)
                        {
                            m[t.l+t.c]=P(m[t.l].first+t.v,t.id);
                            pre[t.id]=m[t.l].second;
                        }
                        if(t.r==0&&m[t.l].first+t.v>ans.first)
                            ans=P(m[t.l].first+t.v,t.id);
                    }
                }
            }
            res=0;
            s[++res]=i;
        }
    }
    vector<int>vec;
    int pos=ans.second;
    while(pos)
    {
        vec.push_back(pos);
        pos=pre[pos];
    }
    printf("%d\n",vec.size());
    for(int i=vec.size()-1;i>=0;i--)printf("%d%c",vec[i],i?' ':'\n');
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值