(贪心+双指针模拟2个优先队列)
题意:已知一个人一定可以AK一套题目,要求他的最小总罚时(从比赛开始到做出这题的时间为该题的罚时)
给出一个n,表示一共有n道题,随后n行a[i]和b[i](表示第i道题需要a[i]的读题时间和b[i]的敲代码时间,保证ac)并且他有个习惯,当剩余题数大于2题时会先读多一题,然后取2题敲代码时间最短的先敲(只有一题自然只能做它)
题解:因为前面花费的时间会对后面影响很大,所以肯定是容易的题即ac题目耗费时间少的先完成,又由于题目有而外的要求(读2题再写)那么我读的第一题必然是读题时间最短的,然后第一道ac的题目可以是读下一题然后ac第一道题,也可以是ac了第二题再进行其他判断(保证每次ac题目罚时最少)
所有我们可以这么做:
第一步: x[]数组保存a的时间,xy[]数组保存a+b的时间,对他们都进行升序排序取(并且要记录下标)
第二步:取最小读题时间的x[q=0]数组第一个数对应的题号(默认先读他了)然后用一个b0保存他的b
第三步:j接下来还需要ac多n-1个题,取题通过比较xy[p].v的值即a2+b2 和x[q].v+b0的值a2+b1
(这里1指前一题,2指后一题,若是后者小b0也需要变为b2)
最后把b0也加上就得到最后一题的ac时间。(代码中t是时间轴,因此每次ac题目ans+=t】
代码如下:
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<cmath> #include<vector> #include<queue> #include<map> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f3f3f #define ll long long const int maxn = 1e5 + 500; struct node { ll v; int index; }xy[maxn], x[maxn]; ll b[maxn]; bool vis[maxn]; bool cmp(node xx, node yy) { return xx.v < yy.v; } int main() { int n; cin >> n; for (int i = 0; i < n; i++) { scanf("%lld%lld", &x[i].v, &b[i]); xy[i].v = x[i].v + b[i]; xy[i].index = i; x[i].index = i; } sort(xy, xy + n, cmp); sort(x, x + n, cmp); int p = 0, q = 0;//2个指针分别是记录xy[],x[]遍历到的位置 ll t = x[q].v, b0 = b[x[q].index]; vis[x[q++].index] = true; //取第一组数据 int cnt = 1; ll ans = 0; while (cnt++ != n) { while (vis[xy[p].index]) p++; while (vis[x[q].index]) q++; if (xy[p].v <= x[q].v + b0) { vis[xy[p].index] = true; t += xy[p++].v; ans += t; } else { vis[x[q].index] = true; t += x[q].v + b0; b0 = b[x[q++].index]; ans += t; } } t += b0; ans += t; cout << ans << endl; return 0; }