zoukankan      html  css  js  c++  java
  • 「中山纪中集训省选组D1T1」最大收益 贪心

    题目描述

    给出(N)件单位时间任务,对于第(i)件任务,如果要完成该任务,需要占用([S_i, T_i])间的某个时刻,且完成后会有(V_i)的收益。求最大收益。

    澄清:一个时刻只能做一件任务,做一个任务也只需要一个时刻。

    输入格式

    第一行一个整数(N),表示可供选择的任务个数。

    接下来的第二到第(N+1)行,每行三个数,其中第(i+1)行依次为(S_i,T_i,V_i)

    输出格式

    输出最大收益

    样例

    样例输入1

    2
    1 1 1
    1 1 2
    

    样例输出1

    2
    

    样例输入2

    3
    1 1 5
    2 2 3
    1 2 4
    

    样例输出2

    9
    

    样例输入3

    6
    1 2 10
    2 3 10
    3 4 10
    4 5 10
    1 1 5
    5 5 6
    

    样例输出3

    46
    

    数据范围

    在所有数据中,(Nleq5000,1leq S_ileq 10^8,1leq V_i leq 10^8)

    题解

    我又诈尸了。
    来到中山纪中集训,省选组的题目第一天就这么神(集训队的题能不神么)……真是(Orz),咱全场几乎爆零成功垫底。

    回到题目,直接说正解。考虑将任务的价值从大到小排序,假设我们已经安排好了前(i-1)个任务,其中要完成的任务集合是(S)。如果要完成第(i)个任务会导致原来处于(S)的任务中的一个无法完成,那么我们果断放弃第(i)个任务(因为如果换做完成(i),并没有腾出更多时间,但却丢失了更多价值),否则我们没有理由不完成(i)个任务。

    那么我们只需要完成一步就好了,那就是判断将任务(i)加入集合(S)后是否能让所有任务都能找到时间做。这个东西我们可以用一个简单粗暴的方法,每次从任务(i)的起始时间开始找,如果这个时间没有被占领,那么占领,结束。否则找到占领该时间的任务,记作(j)。此时就存在一个选择,到底是让(i)去后面找时间还是让(j)找?显然,能拖得更久的任务去尝试更好,也就是结束时间更晚的那一个去找。就这样递归下去,直到不冲突或者超出任务的结束时间为止(超出了当然就得放弃任务了……)。每次最多遇到(i-1)次冲突(因为最多也只有(i-1)个任务),判断一次的复杂度是(O(n))的。

    这里有一个空间上的优化。其实有用的时间其实并不多,我们将他们记为“活跃点”。我们从每个任务的起始时间开始找,找到第一个空位,就将它加入“活跃点”,显然只考虑活跃点的情况下仍然可以达到最优,这样可以把空间也优化到(O(n))(然而代价是时间复杂度有了离散化时的(log))。

    (Code:)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define N 5005
    #define ll long long
    int n;
    struct node
    {
    	int s, t, v;
    }S[N];
    bool cmp1(node a, node b){return a.s < b.s;}
    bool cmp2(node a, node b){return a.v > b.v;}
    int data[N], plc[N];
    int Find(int x, int p)
    {
    	if (p > S[x].t)
    		return 0;
    	if (!plc[p])
    	{
    		plc[p] = x;
    		return 1;
    	}
    	if (S[plc[p]].t < S[x].t)
    		return Find(x, p + 1);
    	if (Find(plc[p], p + 1))
    	{
    		plc[p] = x;
    		return 1;
    	}
    	return 0;
    }
    int main()
    {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++)
    		scanf("%d%d%d", &S[i].s, &S[i].t, &S[i].v);
    	sort(S + 1, S + n + 1, cmp1);
    	for (int i = 1; i <= n; i++)
    		data[i] = max(data[i - 1] + 1, S[i].s);
    	for (int i = 1; i <= n; i++)
    	{
    		S[i].s = lower_bound(data + 1, data + n + 1, S[i].s) - data;
    		S[i].t = upper_bound(data + 1, data + n + 1, S[i].t) - data - 1;
    	}
    	sort(S + 1, S + n + 1, cmp2);
    	ll ans = 0;
    	for (int i = 1; i <= n; i++)
    		if (Find(i, S[i].s))
    			ans += S[i].v;
    	printf("%lld
    ", ans);
    }
    
  • 相关阅读:
    《JavaScript 闯关记》之 BOM
    《JavaScript 闯关记》之单体内置对象
    《JavaScript 闯关记》之基本包装类型
    《JavaScript 闯关记》之正则表达式
    《JavaScript 闯关记》之函数
    《JavaScript 闯关记》之数组
    被「李笑来老师」拉黑之「JavaScript微博自动转发的脚本」
    「前端开发者」如何把握住「微信小程序」这波红利?
    android开发之路13(实际开发常见问题及解决办法ING)
    android开发之路12(android四大组件&Fragment&AsyncTask类)
  • 原文地址:https://www.cnblogs.com/ModestStarlight/p/11285610.html
Copyright © 2011-2022 走看看