题目链接
翻译
给你一个排列,其中有一些位置上的数字被remove了,让你全都放回去。
定义相邻两个数字如果奇偶性不同,\(diff\) 值加 \(1\)。
求数字全都放回去之后 \(diff\) 的值的最小值。
题解
设 \(dp[i][j][k]\) 表示前 \(i\) 个数字放了 \(j\) 个偶数,最后一个数字奇偶性为 \(k\) 的最小 \(diff\)。
之所以可以只用偶数的个数来定义前 \(i\) 个的状态,是因为它是一个排列。最后放完以后,偶数的个数是确定的。
而这个偶数具体是什么对我们这道题而言不影响。所以我们只要关注奇偶性就好。
至于会不会出现哪个地方放进去偶数然后其实没有足够的偶数让你用呢? 这种情况会被后面没被移走的偶数做转移的时候 exclude 掉的。
所以我们只要老老实实地记录 \(j\) 个偶数最好的情况就行,至于这个状态是不是合法的,没有关系的。
最后输出 \(dp[n][n/2][0]\) 和 \(dp[n][n/2][1]\) 中较小者即可。
时间复杂度 \(\mathcal{O}(N^2)\)
代码
#include <bits/stdc++.h>
#define lson l,mid,rt*2
#define rson mid+1,r,rt*2+1
#define LL long long
using namespace std;
const int N = 100;
//dp[i][j][k] 前 i 个位置,偶数个数有 j 个,最后一个位置奇偶性为k最小diff
int dp[N+10][N+10][2];
int n,p[N+10];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
memset(dp,255,sizeof dp);
for (int i = 0;i <= N; i++){
for (int j = 0;j <= N; j++){
for (int k = 0;k < 2; k++){
dp[i][j][k] = 1000;
}
}
}
dp[0][0][0] = dp[0][0][1] = 0;
cin >> n;
for (int i = 1;i <= n; i++){
cin >> p[i];
for (int j = 0;j <= i; j++){
if (j>0 && p[i]%2 == 0){
dp[i][j][0] = min(dp[i-1][j-1][1]+1,dp[i-1][j-1][0]);
}
if (p[i] == 0 || p[i]%2 == 1){
dp[i][j][1] = min(dp[i-1][j][0]+1,dp[i-1][j][1]);
}
}
}
cout << min(dp[n][n/2][0],dp[n][n/2][1]) << endl;
return 0;
}