这个题一眼看上去是一个贪心,但是一个贪心题就一定要用一个贪心的做法来做吗,为什么不能写一个DP呢,这个题我们可以从他修理牛棚的角度来看,价值就是一个木板可以修几个,但是也同时消耗掉了一个木板的长度也就是代价,想到这里,我们肯定就能想到是一个背包了,但是是一个什么背包呢,我们想一下,完全背包要满足的条件是什么是一个东西可以取无数次,且每一次的价值都会加上,但这个题不一样,这个题你无论修一个牛棚多少次,价值一定不变。
所以是01背包。我们可设一个数组dpij。表示在修到第i个牛棚时用j个木板可以修好的最优解。
因此我们可以得到一个状态转移方程
for(int i=1;i<=c;i++) for(int j=1;j<=m;j++) dp[i][j]=min(dp[i-1][j]+data[i]-data[i-1],dp[i-1][j-1]+1);
min函数里第一个参数是不采用新的木板的值,第二个则是用一个木板的值。
因此代码:
#include<bits/stdc++.h> using namespace std; int m,s,c,data[100010],dp[1010][1010]; int main() { scanf("%d%d%d",&m,&s,&c); for(int i=1;i<=c;i++) scanf("%d",&data[i]); sort(data+1,data+1+c); for(int i=1;i<=c;i++) { for(int j=1;j<=m;j++) dp[i][j]=min(dp[i-1][j]+data[i]-data[i-1],dp[i-1][j-1]+1);//因为是从[i-1]递推过来所以可以用滚动数组。 dp[i][0]=0x7ffffff;//因为dp[0][0]是不可以被赋值的,所以要从i=2开始设为无穷大 } printf("%d",dp[c][m]); }
你以为这就完了吗,nonono.
我们知道背包问题的空间是可以简化的。即通过滚动数组,我们看这个题,DP数组的第一个下标都是i-1,在一个表格里说的话就都在它的上一行。
因此可以优化空间,我们再根据01背包的特点可以知道它是从后向前推得因为只有这样才能满足一个东西只能拿一次。
反之如果是完全背包,那我们必须正推,因为,正推就可以根据该行前面已确定的值来确定现在的值。