codeforces Gym 101341 K Competitions

31 篇文章 0 订阅

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值