10271 - Chopsticks (DP)

记得以前做过一道经典的题目,也是选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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值