题目翻译:
给一个只包含-1,0,1的数列,每次操作可以让a[i]+=a[i-1],求最少操作次数使得序列单调不降
数据范围为N<=10^6
思路:线性dp,定义f[i][3],1<=i<=n,当前位置i取-1,0,1,数列1-i满足不下降的最小操作次数
直接看代码注释吧,主要懒
code:
#include<bits/stdc++.h> using namespace std; const int maxn = 1001000; typedef long long ll; const int inf = 0x3f3f3f3f; const ll mod = 1000007; int n; int f[maxn][3], a[maxn]; int main() { //freopen("test.txt", "r", stdin); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", a + i); a[i]++; } memset(f, 0x3f, sizeof(f)); f[1][a[1]] = 0; for (int i = 2; i <= n; i++) { if (a[i] == 0) {//当前位置最初为-1 f[i][0] = f[i - 1][0];//当前位置选-1,前一位置只能选-1,且不需要对当前数字进行修改 //如果初始为-1,而要改成0/1,则要从前一位置为1的情况转移过来 if (a[i - 1] == 2) {//如果前一位置最初就为1,则一开始就能进行操作,所以最终前一位置可以取-1,0,1都可以 f[i][1] = min(f[i - 1][1], f[i - 1][0]) + 1;//最开始就操作一次,变为0, 前一位置可以为-1,0 f[i][2] = min(min(f[i - 1][0], f[i - 1][1]), f[i - 1][2]) + 2;//一开始就操作两次,变为1, 前一位置可以为-1,0,1 } else {//前一位置不为1 f[i][1] = inf;//前一位置不为1,而当前位置还要为0是不可能的,因为这种情况下当前位置数值增大,只能前一位置改变为1,再修改为0,但是10不满足不下降 f[i][2] = f[i - 1][2] + 2;//只能把前一位置先修改为1,再进行两次增加操作 } //cout << i << " " << f[i][0] << " " << f[i][1] << " " << f[i][2] << endl; } else if (a[i] == 1) { f[i][0] = f[i - 1][0] + 1;//为0转变为-1,前一位置只能取-1,而且需要操作一次 f[i][1] = min(f[i - 1][1], f[i - 1][0]);//前一位置可以为-1/0 if (a[i - 1] == 2) {//和上一操作同理,看前一位置的数最开始是不是1,是1就可以一开始就直接对当前位置进行+1操作变为1 //此时前一位置就能取-1,0,1 f[i][2] = min(min(f[i - 1][2], f[i - 1][0]), f[i - 1][1]) + 1; } else {//不然就只能先把前一位置变为1,再进行+1操作,此时前一位置就只能取1 f[i][2] = f[i - 1][2] + 1; } //cout << i << " " << f[i][0] << " " << f[i][1] << " " << f[i][2] << endl; } else {//下面同理了 f[i][0] = f[i - 1][0] + 2; if (a[i - 1] == 0) { f[i][1] = min(f[i - 1][0], f[i - 1][1]) + 1; } else { f[i][1] = f[i - 1][0] + 1; } f[i][2] = min(min(f[i - 1][2], f[i - 1][0]), f[i - 1][1]); //cout << i << " " << f[i][0] << " " << f[i][1] << " " << f[i][2] << endl; } } int ans = min(f[n][2], min(f[n][0], f[n][1])); if (ans >= inf) {//无解 printf("BRAK\n"); } else { cout << ans << endl; } return 0; }