题目链接
http://codeforces.com/contest/822/problem/C
题目大意
给你n个时间段,有起始日期和结束日期,用int型书表示,还有一个目标天数x, 每个时间段有一个cost值,问你从n个时间段取出两个时间段持续时间加起来正好为x,且1.cost总和最小, 2.时间段不能重叠,即ri < lj 或 rj < li;
核心思想
首先要想到可以用一个best[i]数组去记录i长度所需花费的最小cost,我们需要根据输入数据不断地去更新它,使他成立;
我们定义一个结构体P{int p, int len, int cost, int type}pp[M];
p 表示起始日期或结束日期, len表示这个时间段的长度,type则用来标记该时间点是起始日期还是结束日期
我们把pp[M]数组按照p的大小排序, 如果p相同,那么再看type,我们要使结束日期排在起始日期的后面(为什么不能反过来呢?如果碰到起始日期节点更新best数组, 那么我们碰到结束日期节点更新ans时就可能把重叠的时间段算进去了(两段时间段其中一个终点和另一个起点重叠时),这个是本题的关键一定要理解)。这样所有时间段就按照时间点的先后排好序了,我们就可以开始更新答案了
把所有数据扫一遍,如果遇到一个结束日期节点,那么我们就要更新best[i], best[pp[i].len] = min(best[pp[i].len], pp[i].cost);
如果碰到一个起始日期节点,那么我们就要更新ans,
ans = min(ans, pp[i].cost + best[x - pp[i].len]);
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 2e5 + 5;
const int INF = 2e9 + 7;
int a[M], best[M];
struct P
{
int p, len, cost, type;
bool operator < (const P&a)
{
if(p == a.p) return type > a.type;
return p < a.p;
}
}pp[2*M];
int main()
{
int n, x, top = 0;
scanf("%d%d", &n, &x);
for(int i=0; i<n; ++i)
{
int l, r, cst;
scanf("%d%d%d", &l, &r, &cst);
pp[top++] = P{l, r - l + 1, cst, 1};
pp[top++] = P{r, r - l + 1, cst, -1};
}
ll ans = INF;
sort(pp, pp+top);
fill(best, best+x, INF);
for(int i=0; i<top; ++i)
{
if(pp[i].len >= x) continue;
if(pp[i].type == 1)
{
ans = min(ans, (ll)pp[i].cost + (ll)best[x - pp[i].len]);
}
else best[pp[i].len] = min(best[pp[i].len], pp[i].cost);
}
if(ans == INF) ans = -1;
cout<<ans<<endl;
return 0;
}