题意:给一个数字(开头非0),拿掉其中的d个数字,使剩下的数字最大(前后顺序不能变)。
析:拿掉d个数字,还剩下n-d个数字。相当于从n个数字中按先后顺序选出n-d个数字使组成的数字最大,当然采用窗口滑动优先选取大的。
也就是说,当然第一位最大,这个数就最大了,所以这是一个贪心算法。我开始并不知道有这个算法,
所以开始我是暴力的,700ms,要是数据量再大一点,就TLE了。所以我想肯定有高效率的算法,查了查,原来还有这个。
先说我想法,首先在前d个数字中选最大的,然后在从这个数字到d+1个中选最大的,依次往后,贪心。
想法很简单,就是太慢了。然后我再说单调队列,就是用一个队列,里面是从大到小排的,必须是这样的,先对前d-1个进行预处理,
把太小的都删了,然后一个一个的放,放的时候把小于该值的都删了,保证是从大到小的,每次从队首取元素,保证是最大的。
如果还觉得太慢了,可以在删除元素时用二分查找,更快的确定位置。
代码如下:
暴力代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 100000 + 10; int a[maxn]; char s[maxn]; int main(){ // freopen("in.txt", "r", stdin); int n, d; while(scanf("%d %d", &n, &d) == 2 && n){ scanf("%s", s); for(int i = 0; i < n; ++i) a[i] = s[i] - '0'; int indx = 0; int st = 0, ed = d+1; while(true){ int m = -1; for(int i = st; i < ed; ++i){ if(a[i] > m){ m = a[i]; st = i + 1; } if(9 == m) break; } printf("%d", m); if(ed == n) break; ++ed; } printf(" "); } return 0; }
单调队列代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 100000 + 10; struct node{ int id, val; }; char s[maxn]; node a[maxn], q[maxn]; void solve(int k, int n){ int front = 0, rear = -1; for(int i = 0; i < k; ++i){ while(front <= rear && q[rear].val < a[i].val) --rear;//预处理前d个,要保证是单调的,小于的都删掉 q[++rear] = a[i]; } int id = -1; for(int i = k; i < n; ++i){ while(front <= rear && q[rear].val < a[i].val) --rear;//同上 q[++rear] = a[i]; while(q[front].id <= id) ++front;//因为是有顺序的,要往后放 printf("%d", q[front].val); id = q[front].id; } printf(" "); } int main(){ // freopen("in.txt", "r", stdin); int n, d; while(scanf("%d %d", &n, &d) == 2 && n){ scanf("%s", s); for(int i = 0; i < n; ++i){ a[i].id = i; a[i].val = s[i] - '0'; } solve(d, n); } return 0; }