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;
}

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