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;
}
浙公网安备 33010602011771号