题目链接: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 ; }