Codeforces Round #612 (Div. 2) C. Garland(記憶化搜索dp)

題目鏈接: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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章