P4544 [USACO10NOV]购买饲料Buying Feed
约翰开车来到镇上,他要带KK吨饲料回家。运送饲料是需要花钱的,如果他的车上有XX吨饲料,每公里就要花费(X^2) 元,开车D公里就需要(D imes X^2) 元。约翰可以从N家商店购买饲料,所有商店都在一个坐标轴上,第ii家店的位置是(X_i) ,饲料的售价为每吨(C_i) 元,库存为(F_i) 。
约翰从坐标X=0开始沿坐标轴正方向前进,他家在坐标X=E上。为了带K吨饲料回家,约翰最少的花费是多少呢?假设所有商店的库存之和不会少于K。
举个例子,假设有三家商店,情况如下所示:
坐标 X=1 X=3 X=4 E=5
库存 1 1 1
售价 1 2 2
如果K=2,约翰的最优选择是在离家较近的两家商店购买饲料,则花在路上的钱是1+4=5,花在商店的钱是2+2=4,共需要9元。
输入格式
第1行:三个整数K,E,N 第2..N+1行:第i+1行的三个整数代表,(X_i,F_i,C_i)
输出格式
一个整数,代表最小花费
输入
2 5 3
3 1 2
4 1 2
1 1 1
输出
9
说明/提示
(1leq K leq10000,1 leq E leq 500 , 1 leq N leq 500)
(1 leq Fi leq 10000, 1 leq C_i leq 10^7)
容易推出,先对x排序,前i个store买了j吨商品:dp[i][j]
(dp[i][j]=min(dp[i-1][z]+z^2dx+f_i(j-z))),(0<=(j-z)<=c_i)
,(z<=j)
这样是O(nkf)的
也就是多重背包可以用多个01背包暴力出来
我们知道,任意一个整数都能用若干个互不相同的(2^i)表示出来,
令(P=2^0+2^1+2^2+dots+2^i+r),且(r<2^{i+1})
所以,1至((2^{i+1}-1))之间的数都能用若干个互不相同的(2^i)表示出来,
同时加上r,
r+1至((r+2^{i+1}-1)=P)也能表示出来,且每个数最多用一次
这样,p重背包能转化成log(p)个01背包。
在进行暴力01即可。复杂度降到了O(nk*log(f))
#include<bits/stdc++.h>
using namespace std;
template<class T> inline bool read(T &x){
x=0;register char c=getchar();register bool f=0;
while(!isdigit(c)){if(c==EOF)return false;f^=c=='-',c=getchar();}
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(f)x=-x;
return true;
}
template<class T>inline void print (T x){
if(x<0)putchar('-'),x=-x;
if(x>9)print (x/10);
putchar('0'+x%10);
}
#define read(a,b) read(a),read(b)
#define read(a,b,c) read(a),read(b),read(c)
#define Init(a,v) memset(a,v,sizeof(a))
typedef long long ll ;
const ll MAXN=508,inf=0x3f3f3f3f,mod=1e9+7;
ll k,e,n;
struct S{
ll x,f,c;
bool operator<(const S&o)const{return x<o.x;}
}s[MAXN],t[MAXN*15];
ll dp[MAXN*20],cnt;
int main() {
read(k,e,n);
for(ll i=1;i<=n;++i)read(s[i].x,s[i].f,s[i].c);
sort(s+1,s+n+1);//x不是递增的
for(ll i=1;i<=n;++i){//将1~n个多重背包变成1~cnt个01背包。
for(ll j=1;j<=s[i].f;j<<=1){
t[++cnt].c=s[i].c*j;
t[cnt].x=s[i].x;
t[cnt].f=j;
s[i].f-=j;
}
if(s[i].f){//如果有r
t[++cnt].x=s[i].x;
t[cnt].f=s[i].f;
t[cnt].c=s[i].c*s[i].f;
}
}
t[0].x=t[1].x;//第1个商店与上一个距离为0.
Init(dp,inf);
dp[0]=0;
for(ll i=1;i<=cnt;++i){
for(ll j=k;j>=t[i].f;--j){//01背包滚动数组
dp[j]=min(dp[j]+(t[i].x-t[i-1].x)*j*j,
dp[j-t[i].f]+(t[i].x-t[i-1].x)*(j-t[i].f)*(j-t[i].f)+t[i].c);
}
}
printf("%lld",dp[k]+(e-s[n].x)*k*k);//从最后一个商店到e
return 0;
}