单调队列优化的dp,要用到两层单调队列。。做了很久很久。。。原来用long long 运算要比用int慢上很多很多,就是因为我用的longlong 一直超时,改成int就ac了,真是无语啊。。。这题模型挺容易想的,但是数据量超大,肯定不能正常的递推,仔细想想可以发现,其实每一次递推不必用前一层所有满足情况的值去找到该层的最大值,可以用单调队列维护满足条件区间的最大值,这样就可以在O(1)的时间下获得最大值,于是问题就迎刃而解了。
我用的数组模拟的单调队列,写的时候一定要把下标搞清楚,不然会一直wa。dp数组表示的是到达第i层第j个点时的最大值,第一层单调队列维护的是花费时间,因为时间一直是正的,所以直接维护就可以了,然后每次维护满足到达该点的情况的最值用max_val数组来维护,其实这个维护直接用while()维护就可以了,时间复杂度绝对不会超时,当然用二分也可以。事实证明直接维护不比二分慢。。。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define CLR(a, b) memset(a, b, sizeof(a)) #define SL(a) strlen(a) using namespace std; const int N = 111; const int M = 11111; int dp[N][M]; int welc[N][M]; int tim[N][M], max_val[M];//用来模拟单调队列。 int add[M]; /*int B_ser(int l, int r, int key) { int m; while(l <= r) { m = (l + r) >> 1; if(max_val[m] >= key) l = m + 1; else r = m - 1; } return l; }*/ int main() { //freopen("input.txt", "r", stdin); //freopen("output.txt", "w", stdout); int n, m, i, j, l, r, head, tail, t, k, ans; while(scanf("%d%d%d", &n, &m, &k), n + m + k) { CLR(welc, 0);CLR(tim, 0); for(i = 0; i <= n; i ++) { for(j = 1; j <= m; j ++) { scanf("%d", &welc[i][j]); } } for(i = 0; i <= n; i ++) { tim[i][0] = 0; for(j = 1; j <= m; j ++) { scanf("%d", &tim[i][j]); tim[i][j] += tim[i][j - 1]; } } CLR(dp, 0); for(i = 0; i <= n; i ++) { add[0] = head = l = 0; tail = -1; r = 0; while(r <= m) { while(tim[i][r] - tim[i][l] <= k && r <= m) { while(tail >= head && max_val[tail] < dp[i][r] - add[r]) tail --; //tail = B_ser(head, tail, dp[i][r] - add[r]); max_val[++ tail] = dp[i][r] - add[r]; dp[i + 1][r] = max_val[head] + add[r]; add[r + 1] = add[r] + welc[i][r + 1]; r ++; } if(r > m) break; while(tim[i][r] - tim[i][l] > k && l <= r) { if(dp[i][l] - add[l] == max_val[head]) head ++; l ++; } } add[m] = 0; tail = -1;head = 0; l = r = m; while(l >= 0) { while(tim[i][r] - tim[i][l] <= k && l >= 0) { while(tail >= head && max_val[tail] < dp[i][l] - add[l]) tail --; //tail = B_ser(head, tail, dp[i][l] - add[l]); max_val[++ tail] = dp[i][l] - add[l]; dp[i + 1][l] = max(dp[i + 1][l], max_val[head] + add[l]); if(l)add[l - 1] = add[l] + welc[i][l]; l --; } if(l < 0) break; while(tim[i][r] - tim[i][l] > k && l <= r) { if(dp[i][r] - add[r] == max_val[head]) head ++; r --; } } } ans = 0; for(i = 0; i <= m; i ++) ans = max(ans, dp[n + 1][i]); printf("%d\n", ans); } }