[狀態壓縮DP] Poj 3254, Poj 1185

來源:互聯網
上載者:User

    狀態壓縮DP一般適合的題型的特徵為:一個矩陣,行數較大,列數較小,每個點的狀態只有兩種。正好用一個整型數int來表示每行的一種狀態(其實是其二進位形式,每bit的0和1表示每點的狀態)。不同的是各題的狀態dp的定義,狀態間的限制,狀態的轉換方程。

第一道(Poj 3254):

/**題意:在一片M行N列的草地上(用0和1矩陣表示),1表示能放牛,0表示不能放。      在草地上放牛並且牛不能相鄰,問有多少种放法(一頭牛都不放也算一種)。題解:對於每一行來說,放牛的可能數有2^N種,但是根據以下限制條件就能排除很多:      1.每行中的牛不能相鄰,經計算當N=12時,滿足條件的狀態只有377個      2.每行中放牛要滿足草地的硬體條件,只有1處可放,排除一些      3.上一行中與本行對應的位置處不能放牛,排除一些            由於N值最大為12,所以可以用一個位元來表示一行的狀態,這就是“狀態壓縮”了。      定義dp[i][j]:第i行的狀態為state[j]時,前i行能放牛方法的總數.*/#include <cstdio>#include <queue>#include <algorithm>#include <iostream>#include <cstring>using namespace std;#define MOD 100000000int n, m, row[12];int nState, state[1000];int dp[14][1000];void init(){    int k = 1 << n;    nState = 0;    for (int i = 0; i < k; i++)        if ( (i & (i<<1)) == 0 )            state[nState++] = i;}int main(){    int k;    scanf("%d %d", &m, &n);    init();    for (int i = 0; i < m; i++)    {        row[i] = 0;        for (int j = n-1; j >= 0; j--)        {            scanf("%d", &k);            row[i] += k << j;        }    }    // 求dp[0],若state[j]滿足第0行草地的硬體條件,則dp[0][j]=1    for (int j = 0; j < nState; j++)    {        dp[0][j] = ( (row[0] & state[j]) == state[j] ) ? 1 : 0;    }    // 求dp[i],如果state[j]和第i-1行的狀態state[k]不衝突,則dp[i][j]=Σ(dp[i-1][k])    for (int i = 1; i < m; i++)    {        for (int j = 0; j < nState; j++)        {            if ( (row[i] & state[j]) != state[j] )  //判斷是否滿足草地硬體條件                continue;            for (int k = 0; k < nState; k++)            {                if ( dp[i-1][k] && (state[k]&state[j]) == 0 )                    dp[i][j] = (dp[i][j] + dp[i-1][k]) % MOD;            }        }    }    int res = 0;    for (int j = 0; j < nState; j++)    {        if ( dp[m-1][j] )            res = (res + dp[m-1][j]) % MOD;    }    printf("%d\n", res);    return 0;}

第二道(Poj 1185):

/**題意:在一個n行m列的矩陣中,字元'P'處能放炮兵,字元'H'處不能放炮兵。      並且如果兩個炮兵在一條(水平或垂直)直線上時,它們的距離不能小於2,問最多放多少個兵。由於在求第i行時,它的狀態要收到第i-1行和i-2行的影響,所以定義一個三維dp:dp[i][j][k]表示第i行的狀態為state[j],第i-1行的狀態為state[k]時,前i行能放炮兵的最大數量。for ( ... i < n ...){    for ( ... j < nState ... )    {        如果狀態j和第i行的地形不衝突,那麼:        for ( ... k < nState ... )        {            如果第i行狀態j和第i-1行狀態k不衝突,那麼:            for ( ... h < nState ... )            {                如果第i行狀態j和第i-2行狀態h不衝突,那麼:                在dp[i-1][k][h]中找最大的一個賦值給dp[i][j][k],再加上state[j]中1的個數            }        }    }}*/#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;int n, m, dp[105][80][80];      //dp[i][j][k]表示第i行的狀態為j,第i-1行的狀態為k時能放炮兵的最大數量int nState, state[80], num[80]; //10位二進位位中各個1之間的距離不小於2,這樣的數只有60個。                                //依次存放在state[]中,num[i]表示state[i]中1的個數//在小於2^m的數中找1之間的距離不小於2的數,儲存在state[]中void init(){    int k = 1 << m;    nState = 0;    for (int i = 0; i < k; i++)        if ( (i&(i<<1)) == 0 && (i&(i<<2)) == 0 )        {            state[nState] = i;            num[nState] = 0;            int j = i;            while (j)            {                num[nState] += j % 2;                j /= 2;            }            nState++;        }}int main(){    int  row[105];    char str[15];    while ( cin >> n >> m )    {        init();        for (int i = 0; i < n; i++)        {            row[i] = 0;            scanf("%s", str);            for (int j = 0; j < m; j++)                if (str[j] == 'P')                    row[i] += 1 << j;        }        memset(dp, 0, sizeof(dp));        // 計算dp[0]        for (int j = 0; j < nState; j++)        {            if ( (state[j] & row[0]) != state[j] )                continue;            for (int k = 0; k < nState; k++)                dp[0][j][k] = num[j];        }        // 計算dp[1]        if (n > 1)        for (int j = 0; j < nState; j++)        {            if ( (state[j] & row[1]) != state[j] )                continue;            for (int k = 0; k < nState; k++)            {                if ( (state[j] & state[k]) == 0 )                    dp[1][j][k] = dp[0][k][0] + num[j];            }        }        // 計算dp[>1]        for (int i = 2; i < n; i++)        {            for (int j = 0; j < nState; j++)            {                if ( (state[j] & row[i]) != state[j] )                    continue;                for (int k = 0; k < nState; k++)                {                    if ( state[j] & state[k] )                        continue;                    for (int h = 0; h < nState; h++)                    {                        if ( state[j] & state[h] )                            continue;                        if ( dp[i-1][k][h] > dp[i][j][k] )                            dp[i][j][k] = dp[i-1][k][h];                    }                    dp[i][j][k] += num[j];                }            }        }        // 在dp[n-1]中找最大值        int max = 0;        for (int j = 0; j < nState; j++)        {            for (int k = 0; k < nState; k++)                if (max < dp[n-1][j][k])                    max = dp[n-1][j][k];        }        printf("%d\n", max);    }}

---------------------- 更多“狀態壓縮DP”題目... ------------------------

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.