题目链接:E-来硬的_牛客小白月赛92
题目分类:常规DP题,是DP各维表示的状态设对,转移方程就可以直接出的那种
思路:看到题目里面有m个煤炭,有n个铁矿,求最小时间
发现没有魔法的时候,就是一个经典的01背包问题
设dp[i][j]=烧了i个煤炭,获得了j个铁矿时所用的最小时间
转移方程有:dp[i][j]=min(dp[i-1][j](不用第i个煤炭),dp[i-1][j-x[i]]+y[i](用第i个煤炭))
最终答案为dp[n][m]
但是这一题有一个最多释放一次的魔法
所以考虑在原先的dp基础上再开一维
设dp[i][j][k]=烧了i个煤炭,获得了j个铁矿,使用了k次魔法时所用的最小时间
发现k=0时转移方程和之前一样:dp[i][j][0]=min(dp[i-1][j][0](不用第i个煤炭),dp[i-1][j-x[i]][0]+y[i](用第i个煤炭))
k=1时,转移方程也类似:dp[i][j][1]=min(dp[i-1][j][1](不用第i个煤炭),dp[i-1][j-x[i]][1]+y[i](用第i个煤炭),dp[i-1][j-2*x[i]][0]+(y[i]/2)(对第i个煤炭施加魔法并用它))
最终答案为dp[n][m][1],不用min(dp[n][m][0],dp[n][m][1])(虽然是一样的),因为用魔法比不用魔法烧完铁矿的时间一定短
注意数组边界问题就行
代码1(开快读在100ms左右)
#define int long long
int x[M],y[M];
void InfiniteLight() {
int n,m;
read(n);
read(m);
vector<vector<vector<int>>>dp(n+1, vector<vector<int>>(m+1, vector<int>(2,1e18)));
Forl(i,1,n){
read(x[i]);
read(y[i]);
}
Forl(i,0,n){
dp[i][0][0]=dp[i][0][1]=0;
}
Forl(i,1,n){
Forr(j,m,1){
dp[i][j][0]=min(dp[i-1][max(j-x[i],0LL)][0]+y[i],dp[i-1][j][0]);
dp[i][j][1]=min({dp[i-1][j][1],dp[i-1][max(j-2*x[i],0LL)][0]+(y[i]/2LL),dp[i-1][max(j-x[i],0LL)][1]+y[i]});
}
}
println(dp[n][m][1]);
return ;
}
当然,可以发现,由于i的状态只和i-1有关,所以可以使用滚动数组优化掉第一维代码2(开快读在10ms左右)
#define int long long
int x[M],y[M];
void InfiniteLight() {
int n,m;
read(n);
read(m);
vector<vector<int>>dp(m+1,vector<int>(2,1e18));
Forl(i,1,n){
read(x[i]);
read(y[i]);
}
dp[0][0]=dp[0][1]=0;
Forl(i,1,n){
Forr(j,m,1){
dp[j][0]=min(dp[max(j-x[i],0LL)][0]+y[i],dp[j][0]);
dp[j][1]=min({dp[j][1],dp[max(j-2*x[i],0LL)][0]+(y[i]/2LL),dp[max(j-x[i],0LL)][1]+y[i]});
}
}
println(dp[m][1]);
return ;
}

京公网安备 11010502036488号