Codeforces Round #422 (Div. 2) C. Hacker, pack your bags! 排序, 贪心, 暴力(居然可以n方暴力)

题目链接: Hacker, pack your bags!

题目大意

有x天的假期, 有n张旅行票, 每张票有起始时间l, 结束时间r, 花费cost, 想把假期分成两部分出去旅游, 两部分时间不能重合(ri < lj || rj < li), 问最小花费是多少, 如果不能两部分, 输出-1

思路

1. 排序, 贪心

CF官方解法, 效率 O(nlogn2)
设置一个结构体, struct P{int p, len, cost, type};
将每张票(l, r, cost) 表示成两个结构体, P(l, r-l+1, cost, -1), P(r, r-l+1, cost, 1);
设置一个数组best[i], 表示时间长度为i的最便宜的票, 一开始全为INF, 之后边用边更新
将结构体数字排序, 首先按p排序, p相同按type排序, 这样保证了是按时间顺序且同样时间type为-1的在type为1的前面
遍历整个结构体数组
type为1, 则用p[i].cost更新best[p[i].len]
type为-1, 则用p[i].cost+best[x-p[i].len]更新ans
因为数组按照p然后type排序, 保证了更新ans是用到的best[], 里面存储的都是根据时间段在之前的票得出的, 保证时间不会重叠

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 2E5 + 100, INF = 2e9+10;
int n, x, l, r, c;
int best[MAXN];
struct P
{
    int p, len, cost, type;
    bool operator<(const P &x)
    {
        if(p == x.p) return type < x.type;
        return p < x.p;
    }
}p[MAXN*2];


int main()
{
    scanf("%d%d", &n, &x);
    int cnt = 0;
    for(int i=0; i<n; ++i)
    {
        scanf("%d%d%d", &l, &r, &c);
        p[cnt++] = P{l, r-l+1, c, -1};
        p[cnt++] = P{r, r-l+1, c, 1};
    }
    ll ans = INF;
    sort(p, p+cnt);
    fill(best, best+x, INF);

    for(int i=0; i<cnt; ++i)
    {
        if(p[i].type == -1)
        {
            if(p[i].len < x)
            {
                ans = min(ans, (ll)p[i].cost+(ll)best[x-p[i].len]);
            }
        }
        else best[p[i].len] = min(best[p[i].len], p[i].cost);
    }

    if(ans >= INF) ans = -1;
    cout << ans <<endl;

    return 0;
}

2. 暴力

CF上看到好多人都是暴力过去的…
复杂 O(n2) 加了一点微小的优化, 结果在CF上时间和第一种做法一样
开一个vector<pair<int,int>> vp , 长度为i的票存储在vp[i]里面, first表示开始时间l, second表示cost
然后循环1-x, 在两个循环遍历vp[i], vp[x-i], 检查时间是否重叠, 更新答案
把每个vp[i]根据l排序一下, 可以优化一点

代码

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 3E5 + 100, INF = 0X7FFFFFFF;
int n, x, l, r, c;
vector<pair<int, int>> vp[MAXN];


int main()
{
    scanf("%d%d", &n, &x);
    for(int i=0; i<n; ++i)
    {
        scanf("%d%d%d", &l, &r, &c);
        vp[r-l+1].push_back({l, c});
    }
    for(int i=0; i<=x; ++i) sort(vp[i].begin(), vp[i].end());

    int ans = INF;

    for(int d=1; d<x; ++d)
    {
        int mn = INF;
        auto & u=vp[d], &v = vp[x-d];

        for(int i=0, j=0; i<v.size(); ++i)
        {
            while(j<u.size() && u[j].first+d-1<v[i].first)
            {
                if(mn > u[j].second) mn = u[j].second;
                ++j;
            }
            if(mn != INF)
            {
                int s = mn + v[i].second;
                if(ans > s) ans = s;
            }
        }

    }
    if(ans == INF) cout << -1 << endl;
    else cout << ans << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值