Codeforces Round #422 (Div. 2) C. Hacker, pack your bags! 思维排序或二分

题目:http://codeforces.com/contest/822/problem/C

题意:给多个区间,每个区间都有一个权值,求两个长度和为x的区间的最小权值和。

思路1:像这种找两个区间的问题,对于每个区间只需考虑在他左面的或只需考虑在他右面的即可,不需要都考虑,就像区间1 区间2 和区间2 区间1 效果是一样的

维护一个need【i】表示长度为i的区间的最小权值,至于如何保证区间不交,控制一下维护顺序即可,对原区间按左端点从小到大排序,1->n遍历一遍,设当前区间左端点l,右端点r,权值c,因此长度为r-l+1,这时need【i】表示的通过所有右端点小于l的区间(也就是在此区间左边的区间)维护后的值,控制这个维护的顺序可以用优先队列实现,也可以直接排序,还有一种方法就是以右端点为索引保存这个区间,遍历即可。

优先队列写法:171ms

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int le,ri,cost;
    bool operator < (const node u)const
    {
        return ri>u.ri;
    }
}num[200005];
int need[200005];
int cmp(node u,node v)
{
    if(u.le==v.le)
        return u.ri<v.ri;
    return u.le<v.le;
}
priority_queue<node>q;
int main()
{
    int i,n,x,l,r,c,ban;
    cin>>n>>x;
    for(i=0;i<n;i++)
    scanf("%d %d %d",&num[i].le,&num[i].ri,&num[i].cost);
    sort(num,num+n,cmp);
    int minn=2e9+2;
    for(i=0;i<n;i++)
    {
        int now=num[i].ri-num[i].le+1;
        if(now>=x)
            continue;
        if(need[x-now])
        minn=min(minn,need[x-now]+num[i].cost);
        q.push(num[i]);
        if(i!=n-1)
        while(!q.empty())
        {
            node t=q.top();

            if(t.ri>=num[i+1].le)
                break;
            q.pop();
       //     cout<<t.le<<" "<<t.ri<<" "<<t.cost<<endl;
            if(need[t.ri-t.le+1])
            need[t.ri-t.le+1]=min(need[t.ri-t.le+1],t.cost);
            else
            need[t.ri-t.le+1]=t.cost;
        }
    }
    if(minn==2e9+2)
        cout<<-1<<endl;
    else
    cout<<minn<<endl;
    return 0;
}

排序写法:171ms

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int le,ri,cost;
}num[200005],again[200005];
int need[200005];
int cmp(node u,node v)
{
    if(u.le==v.le)
        return u.ri<v.ri;
    return u.le<v.le;
}
int cmp2(node u,node v)
{
    return u.ri<v.ri;
}
int main()
{
    int i,n,x,l,r,c,ban;
    cin>>n>>x;
    for(i=0;i<n;i++)
    scanf("%d %d %d",&num[i].le,&num[i].ri,&num[i].cost),again[i]=num[i];
    sort(num,num+n,cmp);
    sort(again,again+n,cmp2);
    int minn=2e9+1;
    int index=0;
    for(i=0;i<n;i++)
    {
        int now=num[i].ri-num[i].le+1;
        if(now>=x)
            continue;
        if(need[x-now])
        minn=min(minn,need[x-now]+num[i].cost);
        if(i!=n-1)
        while(again[index].ri<num[i+1].le)
        {
            now=again[index].ri-again[index].le+1;
            if(need[now])
                need[now]=min(need[now],again[index].cost);
            else
                need[now]=again[index].cost;
            index++;
        }
    }
    if(minn==2e9+1)
        cout<<-1<<endl;
    else
    cout<<minn<<endl;
    return 0;
}

右端点vector记录写法:202ms(本以为这个是最优的。。。)

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int le,ri,cost;
}num[200005];
int need[200005];
int cmp(node u,node v)
{
    if(u.le==v.le)
        return u.ri<v.ri;
    return u.le<v.le;
}
vector<node>again[200005];
int main()
{
    int i,n,x,l,r,c,ban;
    cin>>n>>x;
    for(i=0;i<n;i++)
    scanf("%d %d %d",&num[i].le,&num[i].ri,&num[i].cost),again[num[i].ri].push_back(num[i]);
    sort(num,num+n,cmp);
    int minn=2e9+1,index=0;
    for(i=0;i<n;i++)
    {
        int now=num[i].ri-num[i].le+1;
        if(now>=x)
            continue;
        if(need[x-now])
        minn=min(minn,need[x-now]+num[i].cost);
        if(i!=n-1)
        for(;index<num[i+1].le;index++)
        {
            for(int j=0;j<again[index].size();j++)
            {
                now=again[index][j].ri-again[index][j].le+1;
                if(need[now])
                    need[now]=min(need[now],again[index][j].cost);
                else
                    need[now]=again[index][j].cost;
            }
        }
    }
    if(minn==2e9+1)
        cout<<-1<<endl;
    else
    cout<<minn<<endl;
    return 0;
}


思路2:二分。和上面思路整体相同,vector【i】记录长度为i的所有区间,遍历每个区间,在长度匹配的区间里二分找合适的,事先求一个权值最小后缀,优化一下即可。


记得上次去西安遇到一个和这个类似的题目,卡了很长时间都没出。现在又是想了半天也没有思路。。。。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值