1. 程式人生 > 實用技巧 >Codeforces Round #295 (Div. 2) B. Two Buttons (DP)

Codeforces Round #295 (Div. 2) B. Two Buttons (DP)

  • 題意:有兩個正整數\(n\)\(m\),每次操作可以使\(n*=2\)或者\(n-=1\),問最少操作多少次使得\(n=m\).

  • 題解:首先,若\(n\ge m\),直接輸出\(n-m\),若\(2*n>=m\),分\(m\)的奇偶判斷一下,如果是奇數就輸出\(n-(m+1)/2+2\),是偶數就輸出\(n-m/2+1\).否則我們就需要用dp來求解,因為是求最小值,所以先初始化將所有值設為\(INF\),\(dp[i]\)表示從\(n\)\(m\)的操作次數最少的最優解,首先需要更新\([1,n]\)的狀態,這個不難寫,\(dp[i]=dp[i+1]+1\),然後我們就可以從\(1\)

    開始列舉到\(m\),而我們當前的狀態\(dp[i]\)可以更新後面的狀態\(dp[i*2]\),這步應該不難想,這兒的難點是我們需要更新一些奇數的狀態,比如\(n=2\),我們剛開始可以更新\(dp[4]\),然後到\(n=3\)的時候發現\(3\)只能通過\(4\)更新得到,而\(dp[4]\)\(dp[2]\)更新過了,所以我們可以通過\(dp[4]\)來更新\(dp[3]\),於是每次遍歷我們更新兩個狀態,一個是自己的狀態\(dp[i]=min(dp[i],dp[i+1]+1)\),一個是後面的數的狀態\(dp[i*2]=min(dp[i*2],dp[i]+1)\).

  • 程式碼:

    int n,m;
    int dp[N];
    
    int main() {
        //ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        n=read(),m=read();
    
        if(n>=m){
            printf("%d\n",n-m);
            return 0;
        }
    
        if(m<=2*n){
            if(m&1){
                int cnt=(m+1)/2;
                printf("%d\n",n-cnt+2);
            }
            else printf("%d\n",n-m/2+1);
            return 0;
        }
        me(dp,INF,sizeof(dp));
        dp[n]=0;
        for(int i=n-1;i>=1;--i) dp[i]=dp[i+1]+1;
    
        for(int i=1;i<=m;++i){
            dp[i]=min(dp[i],dp[i+1]+1);
            dp[i*2]=min(dp[i*2],dp[i]+1);
        }
    
        printf("%d\n",dp[m]);
    
        return 0;
    }