记得以前做过一道经典的题目,也是选k套筷子,那么不难描述状态 ,即用d[i][j]表示在前i根筷子里选j双的最优解, 该题的唯一不同就是多加了一根最长的筷子 , 为了解决这个问题,就要想办法在状态转移的时候排除最长筷子的影响 。
我们还是先来考虑简单情况,假设没有最长的筷子,那么对于每一个状态只有两个决策 :选第i根筷子还是不选 。因为已经排好序了,所以选相邻两根筷子最优,因此如果不选第i根,状态从d[i-1][j]转移而来; 如果选择第i根,状态从d[i-2][j-1] + (a[i]-a[i-1])^2 转移而来 。这是很好理解的,问题的关键是解决最长筷子的影响 !
假如我们按照从小到大的顺序排序之后选择的话,会出现一个问题,选择了第i根筷子作为第j套餐具之后选择哪根筷子做为第j套中最长的呢? 显然,状态乱了,难以转移,因为长的筷子都在后面,你无法预知哪根长筷子已经被选走了 。所以我们要从大到小进行选择,等等,这还不够,别忘了,我们必须要选一根筷子做为最长的,所以加个限制条件就行了: i >= j*3 。
细心的读者应该已经发现了,该题中的j只可能从j-1中转移过来,就像背包一样,该题也可以用滚动数组来求解,d[j]表示当前状态中选j套筷子的最优解 。
细节见代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll INF = 100000000000000; //开ll只是小心,该题不用开ll int T,k,n,a[5000 + 10]; ll d[5000 + 10][1000 + 10]; int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&k,&n); k += 8; for(int i=n;i>=1;i--) scanf("%d",&a[i]); //从大到小排列,为了排除最长筷子的影响,这样对于每一套筷子一定会有最长筷子 。 for(int i=0;i<=n;i++) for(int j=0;j<=k;j++) d[i][j] = ( j == 0 ? 0 : INF); //初始化边界 。 for(int i=3;i<=n;i++) { for(int j=1;j<=k;j++) { if(i >= j*3) //因为有最长筷子的限制,这样就可以忽略掉影响了,因为一定会有足够的最长筷子 。 d[i][j] = min(d[i-1][j],d[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1])); } //对于第i根筷子 不拿 或 拿 } printf("%lld\n",d[n][k]); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/weizhuwyzc000/article/details/47376749