稍微有点思维难度的DP
首先可以明确一个性质,每棵树只会被砍一次.假设砍两次,那么完全可以第一次不砍,第二次砍,这样做是不会影响答案的.同时可以看出,树的生长速度对答案的影响比树初始的高度大.
不妨利用一下贪心的思想,将生长速度最快的树,放到最后一天用dev值最小的机器砍它.
因此我们将树按照生长速度降序排序,将机器按dev值升序排序.
然后从最后一天开始DP
d[i][j][k]
表示在T-i到i天,砍了前j棵树,且用了前k个机器.
转移分为两种,这棵树一直不砍和现在砍这棵树.详见代码
有几点需要注意一下,1.如果树的高度不够某个机器的dev,实际上是不会对答案进行影响的,因为不满足最优性.2.机器是每一天都可以用一次,注意将上一天的状态转移到这一天的k来.
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=155;
const int INF=0x3f3f3f3f;
struct node{
int h,a;
bool operator < (const node &p) const{
return a<p.a;
}
}t[MAXN];
int dp[MAXN][MAXN][MAXN];
class TaroCutting {
public:
int getNumber(vector <int> h,vector <int> a,vector <int> dev,int T){
int n=h.size(),m=dev.size();
memset(dp,0x3f,sizeof dp);
sort(dev.begin(),dev.end());
dp[0][0][0]=0;
for(int i=0;i<n;i++)
t[i].h=h[i],t[i].a=a[i];
sort(t,t+n);
h.clear(),a.clear();
for(int i=0;i<n;i++) h.push_back(t[i].h),a.push_back(t[i].a);
for(int i=0;i<T;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=m;k++){
dp[i+1][j][0]=min(dp[i+1][j][0],dp[i][j][k]);
if(j<n) dp[i][j+1][k]=min(dp[i][j+1][k],dp[i][j][k]+h[j]+T*a[j]);
if(k<m&&j<n)
dp[i][j+1][k+1]=min(dp[i][j+1][k+1],dp[i][j][k]+dev[k]+i*a[j]);
}
return dp[T][n][0];
}
};