【USACO】又买饲料 单调队列dp
题目描述
约翰开车回家,又准备顺路买点饲料了(咦?为啥要说“又”字?)回家的路程一共有 E 公里,
这一路上会经过 N 家商店,第 i 家店里有 F i 吨饲料,售价为每吨 C i 元。约翰打算买 K 吨饲料,他
知道商家的库存是足够的,至少所有店的库存总和不会少于 K。除了购买饲料要钱,运送饲料也是
要花油钱的,约翰的卡车上如果装着 X 吨饲料,那么他行驶一公里会花掉 X2 元,行驶 D 公里需要
DX2 元。已知第 i 家店距约翰所在的起点有 X i 公里,那么约翰在哪些商店买饲料运回家,才能做到
最省钱呢?
输入
• 第一行:三个整数 K,E 和 N,1 ≤ K ≤ 10000,1 ≤ E ≤ 500,1 ≤ N ≤ 500
• 第二行到第 N + 1 行:第 i + 1 行有三个整数 X i ,F i 和 C i ,0 < X i < E,1 ≤ F i ≤ 10000,1 ≤
C i ≤ 10 7
输出
• 单个整数:表示购买及运送饲料的最小费用
样例输入
2 5 3 3 1 2 4 1 2 1 1 1
样例输出
9
提示
在离家较近的两家商店里各购买一吨饲料,
则花在路上的钱是 1 + 4 = 5,花在店里的钱是
2 + 2 = 4
题解:
这题需要一种奇怪的思想:我们知道一顿饲料如果买了,必将一直影响到最后,于是我们将后来的消费就算到状态里去
于是可以定义f[i][j]表示前i个买j吨一直到终点的花费.然后发现可以消维.
保留f[j]即可
定义d[i]为i到终点的距离.
f[j]=min(f[k]+j^2*d[i]-k^2*d[i]+j*F[i])
把k有关的全部都移项移出来,就可以放到单调队列里
单调队列动规:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=505,M=10005; typedef long long ll; struct node { ll x,w,v; }a[N]; ll f[M],q[M],id[M]; bool comp(const node &p,const node &q){return p.x<q.x;} int main() { int n,m,e; scanf("%d%d%d",&m,&e,&n); for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i].x,&a[i].w,&a[i].v); sort(a+1,a+n+1,comp); for(int i=1;i<=n;i++)a[i].x=e-a[i].x; for(int i=1;i<=m;i++)f[i]=2e16; f[0]=0; ll tmp;int l,r; for(int i=1;i<=n;i++) { l=r=1;q[l]=0;id[l]=0; for(int j=1;j<=m;j++) { while(l<r && j-id[l]>a[i].w)l++; tmp=f[j]; f[j]=min(f[j],q[l]+j*j*a[i].x+j*a[i].v); while(l<=r && tmp-j*j*a[i].x-j*a[i].v<=q[r])r--; q[++r]=tmp-j*j*a[i].x-j*a[i].v;id[r]=j; } } printf("%lld\n",f[m]); return 0; }
70分暴力动规:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long ll; 7 const int N=505,M=10005; 8 const ll xy=2e15; 9 struct node 10 { 11 int w,v,x; 12 }a[N]; 13 ll F[N][M]; 14 bool comp(const node &p,const node &q){return p.x<q.x;} 15 int main() 16 { 17 freopen("pp.in","r",stdin); 18 int n,m,e; 19 scanf("%d%d%d",&m,&e,&n); 20 for(int i=1;i<=n;i++)scanf("%d%d%d",&a[i].x,&a[i].w,&a[i].v); 21 a[++n].x=e; 22 sort(a+1,a+n+1,comp); 23 for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)F[i][j]=xy; 24 F[0][0]=0; 25 ll now;int tmp; 26 for(int i=1;i<=n;i++) 27 { 28 for(int j=0;j<=m;j++) 29 { 30 tmp=j-a[i].w>=0?j-a[i].w:0; 31 if(F[i-1][tmp]==xy)break; 32 for(int k=tmp;k<=j;k++) 33 { 34 now=F[i-1][k]+k*k*(a[i].x-a[i-1].x)+a[i].v*(j-k); 35 if(now<F[i][j])F[i][j]=now; 36 } 37 } 38 } 39 printf("%lld\n",F[n][m]); 40 }