POJ 3276 Face The Right Way [反轉 (貪心)] 《挑戰程序設計競賽》 3.2

POJ 3276 Face The Right Way

題目大意:

一N頭站成一排,有的面向前站,也有的面向後站。你有一種可以且只能反轉連續的K頭牛的朝向的機器。少於K頭牛無法使用此機器。你需要讓所有的牛都面朝前站。求一個最小的K,使得反轉的次數M最少。

輸入:

第一行, N
接下來2~N+1 行, 每行一個字符 ‘B’ 或者 ‘F’ 表示這頭牛面朝前還是朝後。

輸出:

K M

題解:

對於一個固定的k,求翻轉所需要的最少次數,最樸素的有一個 O(n2) 的算法:
首先要明確,反轉的次序是不重要的,對同一個區間反轉兩次和不反轉的效果是一樣的。
我們數組a[] 存所有牛的狀態, 0表示面朝前方,1表示面朝後方。
對於第1頭牛,只有 (1,2,...,k) 這一個反轉可以影響它的狀態, 所以如果第一頭牛是’B’,則必須執行翻轉(1,2,...,k) ,改變(1,2,...,k) 這些牛的狀態。這時候,第2頭牛的狀態只與反轉 (2,3,...,k+1) 有關。這就把問題規模爲n 的爲題轉化爲規模爲 n1 的問題。利用此貪心算法,枚舉前nk+1 頭牛, 最後看下第nk+2 n 頭牛的狀態是否全都朝向前方,就可以判斷當前K是否可行, 並給出解。這個算法在Problem A. Oversized Pancake Flipper中寫過。

需要求最小的K, 只需要把K 從1 到 N枚舉一遍。這樣算法的總體複雜度爲O(n3) 。應該是不能接受的。

好在前面的O(n2) 的算法可以優化爲一個 O(n) 的算法:
對於第i 頭牛,它的當前的狀態與第 ik+1 i1 頭牛的位置是否反轉過有關。這裏在第ik+1 頭牛處反轉,意味着(ik+1,...i) 這些牛的狀態都要改變。對於第i 頭牛,只需要關注在ik+1 i1 處反轉的次數sum。
第i頭牛的狀態爲a[i],那麼第i頭牛的當前狀態就爲(sum + a[i] ) % 2, 是奇數則表示面朝後方,需要反轉,偶數則表示面朝前方,不需要反轉。
這樣只需要維護一個sum值 和 記錄以哪些牛爲起始位置反轉過的數組f[]。
sumi+1=sumi+f[i]f[ik+1]

代碼

#include <iostream>
#include <cstring>
#define MAXN 5010
#define INF 0x3f3f3f3f
using namespace std;

int n;
int a[MAXN], f[MAXN];

int solve(int k) {
    memset(f, 0, sizeof(f));
    int ans = 0, sum = 0;
    for (int i = 0; i < n-k+1; i++) {
        if ((a[i] + sum) % 2 != 0) {
            ans++;
            f[i] = 1;
        }
        sum += f[i];
        if (i+1-k >= 0) {
            sum -= f[i+1-k];
        }
    }
    //判斷當前k是否可行
    bool flag = false;
    for (int i = n-k+1; i < n; i++) {
        if ((a[i] + sum) % 2 != 0) {
            flag = true;
            break;
        }
        if (i+1-k >= 0) {
            sum -= f[i+1-k];
        }
    }
    return flag ? INF : ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    char c;
    for (int i = 0; i < n; i++) {
        cin >> c;
        a[i] = (c == 'B' ? 1 : 0);
    }

    int K = 0;
    int M = INF;
    //枚舉n次
    for (int i = 1; i <= n; i++) {
        int tmp = solve(i);
        if (tmp < M) {
            M = tmp;
            K = i;
        }
    }
    cout << K << " " << M << endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章