noip模拟赛 天天寄快递

分析:并不是特别难的一道题,用到了贪心算法.

      首先可以明确的一点是我们要尽量偷贡献最大的数据,要先满足每一个公司的贡献都大于等于K,以这个作为首要条件.那么我们可以先把每个快递公司的快递按照贡献从大到小排序,每次选贡献最大的,满足要求了就考虑下一个快递公司,如果过程中用超过了s个或者处理完后发现还有快递公司没有满足要求就输出无解.

      上面只是满足了第一个要求,这时可能s个还没有用满,那么我们在按照贡献全体排一次序,看哪些没有用加上去就好了.

中途犯了一个很愚蠢的错误:直接把vis数组赋值为i,其实这是不对的,每一个vis应该对应快递的ID,而不是排序后的i!

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int n, m, s, k;
long long ans,tot, cnt, sum, cnt2;
bool flag = false,can[200010];

struct node
{
    long long e, t;
    bool vis;
}a[200010];

bool cmp1(node a, node b)
{
    if (a.e == b.e)
        return a.t < b.t; //实际上是看谁的贡献最大
    return a.e < b.e;
}

bool cmp2(node a, node b)
{
    return a.t < b.t;
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &s, &k);
    for (int i = 1; i <= m; i++)
    {
        long long e, t;
        scanf("%lld%lld", &e, &t);
        if (2 - t < 0)
        {
            a[++tot].e = e;
            a[tot].t = 2 - t;
        }
    }
    sort(a + 1, a + 1 + tot, cmp1);
    if (a[tot].e < n)
    {
        printf("-23333333
");
        return 0;
    }
    for (int i = 1; i <= tot; i++)
    {
        if (can[a[i].e])
            continue;
        sum -= a[i].t;
        cnt++;
        a[i].vis = 1;
        if (cnt > s)
        {
            flag = 1;
            break;
        }
        if (sum >= k)
        {
            ans += sum;
            sum = 0;
            can[a[i].e] = 1;
        }
    }
    for (int i = 1; i <= n; i++)
        if (!can[i])
        {
            flag = 1;
            break;
        }
    if (flag)
        printf("-23333333
");
    else
    {
        sort(a + 1, a + tot + 1, cmp2);
        for (int i = 1; i <= tot; i++)
        {
            if (!a[i].vis)
            {
                cnt++;
                ans -= a[i].t;
                if (cnt == s)
                    break;
            }
        }
        printf("%lld
", ans);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7581391.html