EOJ:Buying Feed【单调队列优化】
——problem:从0出发到E,有一个容量为K的背包,途中有N个商店出售物品,物品价格不同且搬运物品的费用为k*k*D,k为当前背包的重量。求到终点背包容量恰为K的最小费用。
——solution:树形DP
——url:http://202.120.106.94/onlinejudge/problemshow.php?pro_id=440
_______________________________________________________________
明显的DP
转移方程DP[I][J]=MIN{DP[I-1][K]+K*K*(D[I]-D[I-1])+(J-K)*C[I]} J-F[I]<=K<=J
根据题目数据给的范围,500(I的范围)*10000(J的范围)*500(K的范围) 肯定要超时。
于是用单调队列优化
关于单调队列:
队头的元素肯定是最小的(最大的)
取元素操作:
从队头取出元素,直到该元素在要求的范围内(本题即为J-F[I]<=K<=J)
插入元素操作:
若队尾元素比插入的元素要大,则删除。直到比待插入的元素小为止,然后再插入元素。
另外通过本题明确了队列的用法
头指针指向第一个元素,尾指针指向最后一个元素的后一位。若头尾指针相等则队列为空。
#include<stdio.h> #include<memory.h> #include<stdlib.h> #include<queue> using namespace std; #define oo 1LL<<60 #define N 505 #define M 10005 struct node1 { long long x, f, c; } food[N]; struct node2 { long long value, id; } q[M]; long long dp[N][M]; long long head, tail, k, e, n, i, j, l, ans, tmp; int cmp(const void *a, const void *b) { return (*(node1 *) a).x - (*(node1 *) b).x; } long long min(long long a, long long b) { return a < b ? a : b; } long long max(long long a, long long b) { return a > b ? a : b; } void init() { scanf("%lld%lld%lld", &k, &e, &n); for (i = 1; i <= n; i++) scanf("%lld%lld%lld", &food[i].x, &food[i].f, &food[i].c); food[0].x = 0; qsort(food, n + 1, sizeof(food[0]), cmp); } long long work() { long long up_bound, MIN; for (i = 0; i <= n; i++) for (j = 0; j <= k; j++) dp[i][j] = oo; up_bound = 0; dp[0][0] = 0; for (i = 1; i <= n; i++) { up_bound = min(up_bound + food[i].f, k); head = tail = 0; for (j = 0; j <= up_bound; j++) { while (head < tail && q[head].id < max(0, j - food[i].f)) //单调队列优化 head++; tmp = dp[i - 1][j] + j * j * (food[i].x - food[i - 1].x) - j * food[i].c; while (head < tail && q[tail - 1].value > tmp) tail--; q[tail].value = tmp; q[tail++].id = j; MIN = q[head].value; dp[i][j] = MIN + j * food[i].c; } } return dp[n][k]; } int main() { init(); ans = work(); ans += k * k * (e - food[n].x); printf("%lld\n", ans); return 0; }