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\n"); 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\n"); 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\n", ans); } return 0; }