POJ_3670
由于递增和递减是类似的,下面不妨只讨论变成递增序列的情况。
由于Di只有三个数,所以可以考虑将序列分割成三部分,第一部分全部变成1,第二部分全部变成2,第三部分全部变成3。然后我们枚举3开始的位置,这时一共有若干决策,要么前面的全部是1,要么前面有一段2,然后再前面有一段1或者没有,如果每种决策都考察一遍的话整体复杂度就要O(N^2)了,因此考虑优化一下。记当前的位置为i,1的最后一个位置为j,not1[k]表示k以及k之前不是1的数量,not2[k]表示k以及k之前不是2的数量,not3[k]表示k以及k之后不是3的数量,那么我们把决策表示成not2[i]+(not1[j]-not2[j])+not3[i+1],这样如果我们保留着前面所有j中not1[j]-not2[j]的最小值的话,就可以O(1)找到最优决策了。
这个题目还有一个很好的思路就是直接求最长不降子序列,然后用N减去这个值,不过这样最好也只是O(NlogN)的复杂度。
#include<stdio.h> #include<string.h> #include<algorithm> #define MAXD 30010 #define INF 0x3f3f3f3f int N, not3[MAXD], a[MAXD]; void init() { int i; for(i = 1; i <= N; i ++) scanf("%d", &a[i]); } int deal() { int i, not1, not2, ans, opt; not3[N + 1] = 0; for(i = N; i >= 1; i --) not3[i] = not3[i + 1] + (a[i] != 3); ans = not3[1], not1 = not2 = opt = 0; for(i = 1; i <= N; i ++) { if(a[i] != 1) ++ not1; if(a[i] != 2) ++ not2; opt = std::min(opt, not1 - not2); ans = std::min(ans, not2 + opt + not3[i + 1]); } return ans; } void solve() { int i, ans = deal(); for(i = 1; i <= N / 2; i ++) std::swap(a[i], a[N - i + 1]); ans = std::min(ans, deal()); printf("%d\n", ans); } int main() { while(scanf("%d", &N) == 1) { init(); solve(); } return 0; }