uva10271选筷子DP

题目

n n n根长度不相同的筷子,现在有 K K K个人需要使用筷子,每个人需要三根筷子,一个人的贡献为短的两根筷子的差值的平方,即 ( A − B ) 2 (A-B)^2 (AB)2。求 K K K个人选完筷子的最小贡献和。

解题思路

如果每个人只选择两根筷子的话,题目就简单了不少。因为肯定会选择连续的两根筷子,也就是 n n n个排好序的数,选出 k k k对数贡献最小。那么就有

D P [ i ] [ 1 / 0 ] [ j ] DP[i][1/0][j] DP[i][1/0][j]为第 i i i个数,选、不选时,总共有 k k k对数的最小贡献。

则有状态转移方程:

//要选i-1和i这一对数 那么前一个数肯定不能选
DP[i][1][j] = min(DP[i][0][j], DP[i - 1][0][j]);

//不选i-1和i这一对数 那么前一个数可选可不选
DP[i][0][j] = min(DP[i][0][j], DP[i - 1][0][j]);
DP[i][0][j] = min(DP[i][0][j], DP[i - 1][1][j]);

题目要求每个人三根筷子,那么从大到小选,并且加上限制条件 j ∗ 3 < = i j*3<=i j3<=i 即可。

AC代码

#include <bits/stdc++.h>
using namespace std;

const int N = 5005, M = 1010;
int a[5010];
long long dp[N][2][M]; //第i根筷子 选/不选 凑出j双的方案数
void solve()
{
    memset(dp, 0x3f3f3f3f, sizeof dp);
    int k, n;
    scanf("%d %d", &k, &n);
    k += 8;
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    sort(a + 1, a + 1 + n, greater<int>());
    for (int i = 0; i <= 2; i++)
        dp[i][0][0] = 0;
    for (int i = 3; i <= n; i++)
    {
        for (int j = 0; j <= min(i / 3, k); j++)
        {
            if (j >= 1)
                dp[i][1][j] = min(dp[i][1][j], dp[i - 1][0][j - 1] + (a[i - 1] - a[i]) * (a[i - 1] - a[i]));
            dp[i][0][j] = min(dp[i][0][j], dp[i - 1][1][j]);
            dp[i][0][j] = min(dp[i][0][j], dp[i - 1][0][j]);
        }
    }
    cout << min(dp[n][0][k], dp[n][1][k]) << endl;
}
int main()
{

    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值