題目鏈接:https://codeforces.com/contest/1287/problem/C
題目大意:一個1~n的序列,其中有幾個數字缺了,是0,填充這些0,使得結果中相鄰數字奇偶性不同的數量最少。
題目思路:懷疑人生。。感覺自己弱爆了。看了官方題解,其實跟我的思路一致,但我覺得有問題,就是這個貪心。簡單說一下,就是將每一段連續的0看成一段,然後先處理這些段兩端是相同奇偶性或者是到頭的(0是第一個數字或最後一個數字的情況),然後從短到長逐個處理。這種情況的好處是,如果能填上,那麼對答案貢獻是0,能讓結果儘可能少。但是我的問題在於,在這種情況下,如果有一段奇數爲兩端的,和偶數爲兩端的,一樣長,那怎麼辦,如果偶數比較多,可不可能因爲他去幫助了奇數導致他填自己偶數的時候歇逼了。剛纔寫題解的時候突然想到,因爲最後總是能填滿,所以即使一個小夥需要幫忙,那另一個缺口靠另一個小夥肯定能補上。。。
行吧,反正感覺這碼量也多的嚇人,看了qsc的視頻後,恍然大悟。qsc聚聚在視頻裏反覆強調這題的簡單。。唉,難受,感覺他的思路確實非常清晰值得學習。而且他說的轉換我也想到了,反正不要求輸出填完後的結果,直接把奇數看成1,偶數看成0,然後讓你往裏頭填0 1使得相鄰不一樣的最少,那馬上就有內味了啊!一個位就兩種情況,一共100個位,不爆搜對得起這數據範圍嗎??太蠢了。。。
然後講一下到底咋搞。記憶化搜索嘛,dp[i][j][k][p],i表示到了第幾位,j表示還剩幾個偶數要填,k表示還剩多少個奇數要填,p表示上一位到底填的啥,0表示填的偶數,1表示填的奇數,2表示上一位還啥都沒填。然後如果這一位不是0,也就是已經有值,如果是偶數,看看上一位是不是偶數,如果是,直接搜下一個,不是的話,搜完還得+1,因爲偶數和奇數之間有一種不同的奇偶性。剩下的就基本這個思路,真看不懂評論問一嘴,或者直接看qsc視頻,講的非常非常好,非常清楚:qsc講解傳送門
以下是代碼:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 1e2+5;
int a[MAXN],n,dp[MAXN][MAXN][MAXN][5];
int dfs(int pos,int x,int y,int val){
if(pos==n+1)return 0;
if(dp[pos][x][y][val]!=-1)return dp[pos][x][y][val];
if(a[pos]){
if(a[pos]%2==0){
if(val==1)return dfs(pos+1,x,y,0)+1;
else return dfs(pos+1,x,y,0);
}
else{
if(val==0)return dfs(pos+1,x,y,1)+1;
else return dfs(pos+1,x,y,1);
}
}
else{
int minn=1e9;
if(val==0){
if(x)minn=min(minn,dfs(pos+1,x-1,y,0));
if(y)minn=min(minn,dfs(pos+1,x,y-1,1)+1);
}
else if(val==1){
if(x)minn=min(minn,dfs(pos+1,x-1,y,0)+1);
if(y)minn=min(minn,dfs(pos+1,x,y-1,1));
}
else{
if(x)minn=min(minn,dfs(pos+1,x-1,y,0));
if(y)minn=min(minn,dfs(pos+1,x,y-1,1));
}
dp[pos][x][y][val]=minn;
return minn;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n){
int ou=n/2,ji=n/2+n%2;
memset(dp,-1,sizeof(dp));
rep(i,1,n){
cin>>a[i];
if(a[i]&&a[i]%2==0)ou--;
if(a[i]&&a[i]%2==1)ji--;
}
cout<<dfs(1,ou,ji,2);
}
return 0;
}