狀態壓縮DP UVa10817-Headmaster's Headache

狀態壓縮是指使用計算機二進制位來存儲狀態,一般用法是將二進制串當做一個集合,bit位代表集合中的元素,bit位取值表示元素是否在集合中。n位二進制串可以描述2^n種集合(狀態),因此對於n的取值是相當嚴格的。狀態壓縮DP中需要使用各種位運算來描述狀態轉換。所以需要對位運算的使用有一定的瞭解,下面是一些巧妙的運用。

  1. 把一個數字x最靠右的第一個1去掉 : x = x & (x-1)
  2. 判斷數字x中是否存在相鄰的1 : x & (x >> 1)

需要注意,位運算的優先級通常比較低,如果不熟悉這些優先級,建議多使用括號避免出錯。

 


UVa10817-Headmaster's Headache是一道典型的狀態壓縮DP。

題意:
某校有某個教師和n個求職者,需講授s個課程(1 ≤ s ≤ 8, 1 ≤ m ≤ 20, 1 ≤ n ≤ 100)。已知每人工資c(10000 ≤ c ≤ 50000)和能教的課程集合,要求支付最少的工資使得每門課至少有兩個教師能教。在職教師不能辭退。

 

分析:
本題使用狀態壓縮顯然應該講課程作爲要壓縮的狀態,考慮每門課程至少有兩個教師教。狀態定義如下:

  1. 16位長二進制串代表狀態,bit位代表課程,其中低八位代表有一位老師教該課程,高八位代表有兩個老師教該課程。
  2. dp[n][s]代表考慮前n個老師的情況下,維護一個狀態最少需要多少開銷。
  3. 每個老師維護一個狀態,代表其能教導的課程。

此時狀態轉移方程如下:

int changeState(int from, int by)
{
    int first = (~from) & by;                       // 第一個上這門課的老師
    int second = from & by;                         // 第二個上這門課的老師
    int to = (from | first | (second << 8));        // 增加這個老師後的狀態
    return to;
}

 

代碼:
 

// 狀態壓縮DP
#include <iostream>
#include <sstream>
#include <cstdio>
#include <bitset>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_S = 8;
const int MAX_N = 100;
const int MAX_STATE = (1 << 16) - 1;
const int INF_ = 0x3f3f3f3f;

int begin_state;                                    // 初始狀態
int begin_cost;                                     // 初始開銷
int states[MAX_N+1];                                // 應聘者狀態
int costs[MAX_N+1];                                 // 應聘者工資
int dp[MAX_N+1][MAX_STATE+1];                       // 考慮前i個應聘者時,維護每個狀態所需最小開銷

int changeState(int from, int by)
{
    int first = (~from) & by;                       // 第一個上這門課的老師
    int second = from & by;                         // 第二個上這門課的老師
    int to = (from | first | (second << 8));        // 增加這個老師後的狀態
    return to;
}

void input(int s, int m, int n)
{
    begin_state = ((1 << 16) - 1) - ((1 << (s+8))-1) + ((1 << 8) - 1) - ((1 << s) - 1);
    begin_cost = 0;
    for (int i = 0; i < m; i++)
    {
        string str;
        int cost, lesson;
        cin >> cost;
        begin_cost += cost;
        getchar();
        getline(cin, str);
        stringstream ss(str);
        while (ss >> lesson)
        {
            lesson--;
            begin_state = changeState(begin_state, (1 << lesson));
        }
    }
    for (int i = 1; i <= n; i++)
    {
        string str;
        int state = 0, lesson;
        cin >> costs[i];
        getchar();
        getline(cin, str);
        stringstream ss(str);
        while (ss >> lesson)
        {
            lesson--;
            state = changeState(state, (1 << lesson));
        }
        states[i] = state;
    }
}

int solve(int s, int m, int n)
{
    memset(dp, INF_, sizeof(dp));
    dp[0][begin_state] = begin_cost;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= MAX_STATE; j++)
        {
            int to_state = changeState(j, states[i]);
            dp[i][j] = min(dp[i][j], dp[i-1][j]);                           // 不增加這個老師
            dp[i][to_state] = min(dp[i][to_state], dp[i-1][j]+costs[i]);    // 增加這個老師
        }
    }
    return dp[n][MAX_STATE];
}

int main()
{
    int s, m, n;
    while (true)
    {
        cin >> s >> m >> n;
        if (s == 0) break;
        input(s, m, n);
        cout << solve(s, m, n) << endl;
    }
}

 

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