题目
n(n<=1e5)件商品,
第i件商品,要么去商店A买,花费ai(ai<=1e4),要么去商店B买,花费bi(bi<=1e4)
两个商店有不同的优惠活动,求买n件商品的最小代价和是多少
- 商店
A
的优惠活动:- 购买商品满三件及以上商品可以打七折(向下取整),不满三件则无任何优惠
- 商店
B
的优惠活动:- 每购买三件商品,可以免去其中价格最低的一件商品的价格
题解
直接粘自己在leetcode写的题解好了(摆烂)
4.10 招商银行专场 题解【不会dp不改名】 - 力扣(LeetCode)
dp[i][4][3]表示前i个,a数组取了0/1/2/>=3个,b数组发生囤积的个数有0/1/2个时的最小代价和。
分两次dp进行,
第一次钦定取够3个(转移按0.7倍转移,只把a>=3的终态当合法状态)。
第二次钦定不够3个(转移按原价转移,只把a<3的终态当合法状态)。
转移时,考虑让第i个选a或是选b。
特别地,当b已经屯了两个的时候,选了第三个,会让第三个免费。
代码是先把代价*7,*10,最后总代价除以10。
代码
class Solution {
public:
typedef long long ll;
ll dp[2][4][3];
struct node{
int a,b;
node(){}
node(int aa,int bb):a(aa),b(bb){}
friend bool operator<(node a,node b){
return a.b>b.b;
}
};
void ckmin(ll &x,ll y){
x=min(x,y);
}
int goShopping(vector<int>& a, vector<int>& b) {
int n=a.size();
vector<node>c;
for(int i=0;i<n;++i){
c.push_back(node(a[i],b[i]));
}
sort(c.begin(),c.end());
memset(dp,127,sizeof dp);
dp[0][0][0]=0;
for(int i=0;i<n;++i){
int nex=(i+1)&1,now=i&1;
for(int j=0;j<=3;++j){
for(int k=0;k<3;++k){
ckmin(dp[nex][min(j+1,3)][k],dp[now][j][k]+c[i].a*7ll);
ckmin(dp[nex][j][(k+1)%3],dp[now][j][k]+(k==2?0:c[i].b*10ll));
}
}
memset(dp[now],127,sizeof dp[now]);
}
ll ans=1e10;
for(int k=0;k<3;++k){
ckmin(ans,dp[n&1][3][k]);
}
memset(dp,127,sizeof dp);
dp[0][0][0]=0;
for(int i=0;i<n;++i){
int nex=(i+1)&1,now=i&1;
for(int j=0;j<3;++j){
for(int k=0;k<3;++k){
if(j+1<3)ckmin(dp[nex][j+1][k],dp[now][j][k]+c[i].a*10ll);
ckmin(dp[nex][j][(k+1)%3],dp[now][j][k]+(k==2?0:c[i].b*10ll));
}
}
memset(dp[now],127,sizeof dp[now]);
}
for(int j=0;j<3;++j){
for(int k=0;k<3;++k){
ckmin(ans,dp[n&1][j][k]);
}
}
return ans/10;
}
};