题目链接:
http://codeforces.com/problemset/problem/747/D
题目大意:
接下来的n天内每天都有一个气温,如果某天的温度为负数,则必须使用冬季轮胎;而温度非负,则既可以使用冬季轮胎,也可以使用夏季轮胎。开始的时候车子上装的是夏季轮胎,如果连续两天使用的轮胎类型不同,则需要换胎。规定使用冬季轮胎的总天数不能超过k,问换胎的最少次数。(注:n天结束后车子上装的既可以是冬季轮胎,也可以是夏季轮胎)。
解题思路:
一开始想到dp+滚动数组。然而时间复杂度太高,查了tutorial发现可以贪心。具体来说就是:
1.当温度为负数的天数之和大于k时,无解。
2.否则令k减去温度为负数的天数之和。并求出所有连续的温度为负数的区段的个数cnt,此时换胎次数x最多为cnt*2。
3.求出所有连续的温度为非负数的区段,并将这样的区段的长度push到一个小顶堆中。先不考虑持续到第n天的非负区段(如果有的话),不要将这样的区段放入堆中(之后判断这种特殊情况)。也不必考虑从第1天开始持续到第一个负区段之前的非负区段,因为一开始车子上的是夏季轮胎,这样的区段不会影响最终结果。
4.当堆不空并且k大于等于堆顶元素的时候,将堆顶元素pop出来,同时将k减去堆顶元素,并且令x -= 2。(相当于贪心地合并掉非负区段)
5.考虑特殊情况:如果存在一个非负区段并且这个区段持续到第n天,并且此时k大于等于这个区段的长度,则可以x--(相当于最后一次不换回夏季轮胎,省掉一次换胎操作);或者最后一个负区段持续到第n天,这种情况也可以省掉一次换回夏季轮胎的操作,也要x--。总之,无论持续到n天的最后一个区段是负区段还是非负区段,都要x--。这样就得到了最终答案。
其实思路不是很难想,但是实现起来需要注意一些细节。
代码写得不是很漂亮:
1 #include <iostream> 2 #include <cstdio> 3 #include <queue> 4 #include <functional> 5 #include <vector> 6 using namespace std; 7 8 int a[200005], n, k; 9 int find_non_negtive_start(int pos) 10 { 11 for (int i = pos; i < n; i++) 12 { 13 if (a[i] >= 0) 14 return i; 15 } 16 return -1; 17 } 18 int find_non_negtive_end(int pos) 19 { 20 for (int i = pos; i < n; i++) 21 { 22 if (a[i] < 0) 23 return i - 1; 24 } 25 return -1; 26 } 27 int find_negtive_start(int pos) 28 { 29 for (int i = pos; i < n; i++) 30 { 31 if (a[i] < 0) 32 return i; 33 } 34 return -1; 35 } 36 int find_negtive_end(int pos) 37 { 38 for (int i = pos; i < n; i++) 39 { 40 if (a[i] >= 0) 41 return i - 1; 42 } 43 return -1; 44 } 45 int main() 46 { 47 priority_queue<int, vector<int>, greater<int> > q; 48 cin >> n >> k; 49 int cnt = 0, cnt_negtive = 0; 50 int start = -1, begin = -1; 51 for (int i = 0; i < n; i++) 52 { 53 scanf("%d", &a[i]); 54 if (a[i] < 0) 55 { 56 cnt++; 57 if (begin == -1) 58 begin = i; 59 } 60 else 61 { 62 if (begin != -1 && start == -1) 63 start = i; 64 } 65 } 66 if (cnt > k) 67 { 68 cout << "-1" << endl; 69 return 0; 70 } 71 if (cnt == 0) 72 { 73 cout << "0" << endl; 74 return 0; 75 } 76 if (start == -1) 77 { 78 cout << "1" << endl; 79 return 0; 80 } 81 if (begin == -1) 82 { 83 cout << "0" << endl; 84 return 0; 85 } 86 cnt_negtive = cnt; 87 cnt = 0; 88 while (1) 89 { 90 int pos = find_negtive_end(begin + 1); 91 if (pos == -1) 92 { 93 cnt++; 94 break; 95 } 96 cnt++; 97 begin = find_negtive_start(pos + 1); 98 if (begin == -1) 99 break; 100 } 101 cnt *= 2; 102 int res = -1; 103 while (1) 104 { 105 int pos = find_non_negtive_end(start + 1); 106 if (pos == -1) 107 { 108 res = n - start; 109 break; 110 } 111 q.push(pos - start + 1); 112 start = find_non_negtive_start(pos + 1); 113 if (start == -1) 114 { 115 break; 116 } 117 } 118 k -= cnt_negtive; 119 while (!q.empty()) 120 { 121 if (k >= q.top()) 122 { 123 k -= q.top(); 124 q.pop(); 125 cnt -= 2; 126 } 127 else 128 { 129 break; 130 } 131 } 132 if (k >= res) 133 { 134 cnt--; 135 } 136 cout << cnt << endl; 137 return 0; 138 }