Problem
codeforces.com/gym/101341/problem/K
vjudge.net/contest/162325#problem/K
Meaning
有 n 场比赛,每一场有:开始时间 a、结束时间 b、价值 c。问在这 n 场中挑选若干场,在使得总的价值最大的前提下,总的时长最短,要求任意两场比赛时间不能有重叠的部分(不算边界)。输出比赛场数、总价值、总时长、所选比赛编号。
Analysis
差不多就是《挑战程序设计竞赛(2e)》第 246 页的那题,虽然那题是网络流,但后面也有提这题的解法。
定义状态:
dp[i]:在时间 [ 0 , i ] 内能得到的最大价值;
len[i]:在得到上述最大价值的情况下的最短时长。
状态转移:用结束时间就是 i 的那些比赛来更新 dp[i]
dp[i] = max { dp[i-1] , dp[ a[j] ] + c[j] | 1 <=j <= n , b[j] = i } (j 表示第 j 个比赛,a、b、c 的意义同题目描述)
如果在 j 点更新了 dp[i],就有:len[i] = len[ a[j] ] + ( b[j] - a[j] );
若选 j 点得到与 dp[i] 一样的值,但能有更短时间,也要更新 len[i]。
时间范围很大,要进行离散化。
还要用个数组记录下当前时刻 i 选的是哪场比赛,好记录路经。
Code
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 200000;
struct node
{
int a, b, c;
int l; // length = b - a
int id; // 比赛编号,因为要排序,不能直接用下标表示编号
node() {}
node(int _b): b(_b) {}
bool operator < (const node &rhs) const
{
return b < rhs.b;
}
} com[N];
int ab[N<<1]; // 离散化数组
int which[N<<1|1]; // 时刻i所选的比赛,记的是下标而不是编号
long long dp[N<<1|1], len[N<<1|1];
stack<int> stk;
int main()
{
int n;
scanf("%d", &n);
int top = 0;
for(int i=0; i<n; ++i)
{
scanf("%d%d%d", &com[i].a, &com[i].b, &com[i].c);
com[i].l = com[i].b - com[i].a;
com[i].id = i + 1;
ab[top++] = com[i].a;
ab[top++] = com[i].b;
}
/* 离散化时刻 */
sort(ab, ab + top);
top = unique(ab, ab + top) - ab;
for(int i=0; i<n; ++i)
{
com[i].a = lower_bound(ab, ab+top, com[i].a) - ab + 1;
com[i].b = lower_bound(ab, ab+top, com[i].b) - ab + 1;
}
/* 按结束时间排序,可二分找到结束时间为i的那些比赛 */
sort(com, com + n);
dp[0] = len[0] = 0;
which[0] = -1;
for(int i=1; i<=top; ++i)
{
dp[i] = dp[i-1];
len[i] = len[i-1];
which[i] = which[i-1];
int beg = lower_bound(com, com+n, node(i)) - com;
for(int j=beg; j<n && com[j].b==i; ++j)
if(dp[i] < dp[com[j].a] + com[j].c)
{
dp[i] = dp[com[j].a] + com[j].c;
len[i] = len[com[j].a] + com[j].l;
which[i] = j;
}
else if(dp[i] == dp[com[j].a] + com[j].c &&
len[i] > len[com[j].a] + com[j].l)
{
len[i] = len[com[j].a] + com[j].l;
which[i] = j;
}
}
for(int i=top; ~which[i]; i=com[which[i]].a)
stk.push(com[which[i]].id);
printf("%d %I64d %I64d\n", stk.size(), dp[top], len[top]);
for( ; stk.size(); stk.pop())
printf("%d%c", stk.top(), stk.size()==1?'\n':' ');
return 0;
}