题解:P9468 [EGOI 2023] Candy / 糖果
有一个很妙的思路,分享一下。
根据题目的意图,我们要在移动距离最短前提下,使得数组的前
设选中的
将元素从
显然不管怎么交换,原来位置和
设交换次数为
可得对于前
所以,最小交换次数的计算方法如下:
要使得交换次数
所以,我们要选中
不难想到这一步可以用 dp 解决。设
转移方程比较简单,只要对于每个不同的元素
转移时可以滚动数组,不过要倒序枚举
时间复杂度约为
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll N, F, T;
ll a[105], dp[105][5100]; // dp[j][k]:(当前第i个数)选了j个,下标和为k时,数值和的最大值
void solve()
{
cin >> N >> F >> T;
for(int i = 1; i <= N; i ++){
cin >> a[i];
}
memset(dp, -0x3f, sizeof dp);
dp[0][0] = 0;
for(int i = 1; i <= N; i ++){
for(int j = min(1LL*i, F); j >= 1; j --){
for(int k = i; k <= 5050; k ++){ // 100*(100+1)/2 = 5050
dp[j][k] = max(dp[j][k], dp[j - 1][k - i] + a[i]);
}
}
}
int minp = 1e9;
for(int k = 0; k <= 5050; k ++){
if(dp[F][k] >= T) minp = min(minp, k);
}
if(minp == 1e9) puts("NO"); else cout << max(0LL, 1LL*minp - F*(F+1)/2) << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solve();
return 0;
}