题目链接:https://codeforces.com/contest/1287/problem/C
题目大意:有一个序列是1-n的置换。有若干个数被拿走。用0表示,现在让你把这几个数放回去。如果相邻的两个数奇偶性不同,那么贡献+1.现在让你放回去要求贡献最小。
思路:直接dp:
dp[i][j][k][0/1]: 前i个数剩余j个奇数k个偶数,当前数为偶/奇时的最小贡献。
因为j+k恒定,可以降维。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
int a[105];
int f[105][55][55][2];
int main()
{
int n, s1=0, s2=0, s3=0;
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
s3+=a[i];
if(a[i]%2==1){
s1++;
}
else if(a[i]!=0){
s2++;
}
}
memset(f, -1, sizeof(f));
s1=n/2-s1+(n%2==1?1:0);
s2=n/2-s2;
if(a[1]==0){
if(s1>0){
f[1][s1-1][s2][1]=0;
}
if(s2>0){
f[1][s1][s2-1][0]=0;
}
}
else{
f[1][s1][s2][a[1]%2]=0;
}
for(int i=2; i<=n; i++){
if(a[i]==0){
for(int j=0; j<=50; j++){
for(int k=0; k<=50; k++){
if(f[i-1][j][k][0]!=-1){
if(j>0){
if(f[i][j-1][k][1]==-1){
f[i][j-1][k][1]=f[i-1][j][k][0]+ 1;
}
else{
f[i][j-1][k][1]=min(f[i-1][j][k][0]+ 1, f[i][j-1][k][1]);
}
}
if(k>0){
if(f[i][j][k-1][0]==-1){
f[i][j][k-1][0]=f[i-1][j][k][0];
}
else{
f[i][j][k-1][0]=min(f[i-1][j][k][0], f[i][j][k-1][0]);
}
}
}
if(f[i-1][j][k][1]!=-1){
if(j>0){
if(f[i][j-1][k][1]==-1)
f[i][j-1][k][1]=f[i-1][j][k][1];
else{
f[i][j-1][k][1]=min(f[i-1][j][k][1], f[i][j-1][k][1]);
}
}
if(k>0){
if(f[i][j][k-1][0]==-1)
f[i][j][k-1][0]=f[i-1][j][k][1]+ 1;
else{
f[i][j][k-1][0]=min(f[i-1][j][k][1]+ 1, f[i][j][k-1][0]);
}
}
}
}
}
}
else{
for(int j=0; j<=50; j++){
for(int k=0; k<=50; k++){
if(f[i-1][j][k][0]!=-1){
if(f[i][j][k][a[i]%2]==-1)
f[i][j][k][a[i]%2]=f[i-1][j][k][0]+((a[i]%2==1)?1:0);
else{
f[i][j][k][a[i]%2]=min(f[i-1][j][k][0]+((a[i]%2==1)?1:0), f[i][j][k][a[i]%2]);
}
}
if(f[i-1][j][k][1]!=-1){
if(f[i][j][k][a[i]%2]==-1)
f[i][j][k][a[i]%2]=f[i-1][j][k][1]+((a[i]%2==1)?0:1);
else{
f[i][j][k][a[i]%2]=min(f[i-1][j][k][1]+((a[i]%2==1)?0:1), f[i][j][k][a[i]%2]);
}
}
}
}
}
}
if(f[n][0][0][0]==-1){
printf("%d\n", f[n][0][0][1]);
}
else if(f[n][0][0][1]==-1){
printf("%d\n", f[n][0][0][0]);
}
else{
printf("%d\n", min(f[n][0][0][1], f[n][0][0][0]));
}
return 0;
}