专栏文章
题解:P10715 【MX-X1-T3】「KDOI-05」简单的序列问题
P10715题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minowfa7
- 此快照首次捕获于
- 2025/12/02 05:57 3 个月前
- 此快照最后确认于
- 2025/12/02 05:57 3 个月前
看起来是 dp 的样子。
显然我们可以对 同时取模 ,是没有影响的。则 就成了一个 01 序列。
发现:
- 每次交换一定是 和 交换(交换相同的显然没必要),进而想到交换操作是不会变化 数组中 的总量的。
- 每个位置最多被交换一次(交换多次显然不优,如 ,没必要),即每个位置只有两种可能:变或不变。
- 发现代价很有特点,交换 代价为 ,我们不妨拆开考虑:令改变位置 的代价是 。
那么问题就换成了一个“放数”的问题,每个位置可以放 或 ,但不能超过序列中原本 的数量,这可以用序列上的 dp 模式解决。
设 表示前 个数,放了 个 ,且当前 的最小代价。
考虑分类讨论当前位置是放 还是放 转移:
最后别忘了加上每次操作的代价,根据放的数是否和原数相同算即可。
时间复杂度 。
CPP#include <bits/stdc++.h>
using namespace std;
const int inf = 2e9;
int n, a[505], c[505], dp[505][505][505];
signed main()
{
ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T; cin >> T;
while(T --){
cin >> n;
int s = 0; // 记录1的总个数
for(int i = 1; i <= n; i ++){
cin >> a[i]; a[i] &= 1; s += a[i];
}
for(int i = 1; i <= n; i ++){
cin >> c[i];
}
for(int i = 0; i <= n; i ++){
for(int j = 0; j <= n; j ++) for(int k = 0; k <= n; k ++) dp[i][j][k] = inf;
}
dp[0][0][0] = 0;
for(int i = 0; i <= n-1; i ++){
for(int j = 0; j <= n; j ++){
for(int k = 0; k <= n; k ++){
if(dp[i][j][k] == inf) continue;
dp[i+1][j][k+(j&1)] = min(dp[i+1][j][k+(j&1)], dp[i][j][k] + c[i+1]*(a[i+1]!=0)); // 放0
dp[i+1][j+1][k+(j+1&1)] = min(dp[i+1][j+1][k+(j+1&1)], dp[i][j][k] + c[i+1]*(a[i+1]!=1)); // 放1
}
}
}
for(int i = 0; i <= n; i ++){
int ans = dp[n][s][i];
cout << (ans == inf? -1 : ans) << " \n"[i == n];
}
}
return 0;
}
警示后人:多测一定不要用 memset,按照输入的 清空,不然 T 到起飞!!!
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...