Beaver's Calculator 【归并排序+贪心】

来源:互联网 发布:四大名助 综艺 知乎 编辑:程序博客网 时间:2024/06/04 19:45
问题描述
  从万能词典来的聪明的海狸已经使我们惊讶了一次。他开发了一种新的计算器,他将此命名为"Beaver's Calculator 1.0"。它非常特别,并且被计划使用在各种各样的科学问题中。
  为了测试它,聪明的海狸邀请了n位科学家,编号从1到n。第i位科学家给这个计算器带来了 ki个计算题。第i个科学家带来的问题编号1到n,并且它们必须按照编号一个一个计算,因为对于每个问题的计算都必须依赖前一个问题的计算结果。
  每个教授的每个问题都用一个数 ai, j  来描述,i(1≤i≤n)是科学家的编号,j(1≤j≤ki )是问题的编号, ai, j  表示解决这个问题所需资源单位的数量。
  这个计算器非常不凡。它一个接一个的解决问题。在一个问题解决后,并且在下一个问题被计算前,计算器分配或解放资源。
  计算器中最昂贵的操作是解放资源,解放远远慢于分配。所以对计算器而言,每一个接下来的问题所需的资源不少于前一个,是非常重要的。
  给你关于这些科学家所给问题的相关信息。你需要给这些问题安排一个顺序,使得“坏对”尽可能少。
  所谓“坏对”,就是相邻两个问题中,后一个问题需求的资源比前一个问题少。别忘了,对于同一个科学家给出的问题,计算它们的相对顺序必须是固定的。
输入格式
  第一行包含一个整数n,表示科学家的人数。接下来n行每行有5个整数,ki,ai, 1, xi, yi,mi (0 ≤ ai, 1 < mi ≤ 109, 1 ≤ xi, yi ≤ 109) ,分别表示第i个科学家的问题个数,第1个问题所需资源单位数,以及3个用来计算ai, j 的参量。ai, j = (ai, j - 1 * xi + yi)mod mi。
输出格式
  第一行输出一个整数,表示最优顺序下最少的“坏对”个数。
  如果问题的总个数不超过200000,接下来输出 行,表示解决问题的最优顺序。每一行两个用空格隔开的整数,表示这个问题所需的资源单位数和提供这个问题的科学家的编号。
样例输入
2
2 1 1 1 10
2 3 1 1 10
样例输出
0
1 1
2 1
3 2
4 2
数据规模和约定

1 ≤ n ≤ 5000, 1 ≤ ki ≤ 5000。

#include<stdio.h>#include<string.h>#include<stdlib.h>struct sc{int cnt, count;long long num[5001]; }p[5010];struct ms{int id;long long num;}ans[200000], t[200000];int sta, end;void merge(int x, int y){int mid = (x+y)>>1, i = x, j = mid+1, k = x;while(i != mid+1 && j != y+1){if(ans[i].num > ans[j].num){t[k].id = ans[j].id;t[k++].num = ans[j++].num;}else{t[k].id = ans[i].id;t[k++].num = ans[i++].num;}}while(i <= mid){t[k].id = ans[i].id;t[k++].num = ans[i++].num;}while(j <= y){t[k].id = ans[j].id;t[k++].num = ans[j++].num;}for(i = x; i <= y; ++i){ans[i].id = t[i].id;ans[i].num = t[i].num;}}void mergesort(int x, int y){if(x < y){int mid = (x+y)>>1;mergesort(x,mid);mergesort(mid+1,y);merge(x,y);}}int main(){int n, i, j, k, l = 0, m, sum = 0;scanf("%d", &n);for(i = 0; i < n; ++i){long long t1, t2, t3, t4;k = 0;scanf("%d %lld %lld %lld %lld", &m, &t1, &t2, &t3, &t4);l += m;p[i].cnt = m;p[i].count = 1;p[i].num[0] = -1;p[i].num[1] = t1;for(j = 2; j <= m; ++j){p[i].num[j] = (p[i].num[j-1] * t2 + t3) % t4;if(p[i].num[j] < p[i].num[j-1])k++;}if(k > sum)sum = k;}printf("%d\n", sum);if(l <= 200000){sta = 0;end = 0;while(end < l){for(i = 0; i < n; ++i){for(j = p[i].count; j <= p[i].cnt; ++j){if(j != p[i].count && p[i].num[j] < p[i].num[j-1]){p[i].count = j;break;}ans[end].id = i;ans[end++].num = p[i].num[j];}if(j > p[i].cnt)//all ascending p[i].count = j;}mergesort(sta,end-1);sta = end;}for(i = 0; i < l; ++i)printf("%lld %d\n", ans[i].num, ans[i].id+1);}return 0;}

题意:有n个科学家,每个科学家有m个问题。现在需要计算所有科学家的所有问题,计算每个问题都需要付出一定的资源,并且计算的每个科学家的问题的相对顺序不能改变。当计算前一个问题所需的资源大于后一个问题所需的资源我们称之为一对“坏对”,现在要求最少的坏对数,以及计算问题的顺序,输出每个问题所消耗的资源以及该问题所属于的科学家的编号。

思路:首先我们知道每个科学家的问题的相对顺序无法改变,所以对于每一个科学家的问题其本身的序列就有可能存在坏对,算出每一个科学家的问题的坏对数,记录其中最大的那一个。记为sum,试想无论如何排序,由于科学家问题的顺序限制,所以坏对数最小的值就是sum。那么我们能否在sum坏对数的情况下,合理的安排所有科学家的问题顺序。

答案当然是可以得。我们可以将整个序列进行分层,第一层也就是每一个科学家的问题序列第一次出现坏对的时候(有可能从未出现,那么就是整个序列),我们将这些科学家的第一层的问题统一放到一个数组里进行归并排序,排序完成后再将其存入最终的答案中作为最终答案的第一层序列。第二层以至第sum层都是如此,那么这个序列就是我们要的答案,而且坏对数保证就是sum。

证明:首先对于每一个科学家自身的序列来说,由于我们用的是归并排序,所以绝对不会改变每一个科学家自身序列的相对顺序。其次对于每一层的序列,我们经过排序后肯定是保证严格递增的(即不存在坏对),对于下一层的序列来说同样如此,由于下一层的序列的问题中和上一层的序列的问题中至少存在着一个坏对,所以对于下一层的第一个数(也是最小的那个数)必定是小于上一层的最后一个数的,也就是说保证了2层之间有一个坏对数。那么最终答案就是sum个坏对数,也就是最少的坏对数。

1 0
原创粉丝点击