1. 程式人生 > 其它 >ccpc威海 D-Sternhalma(狀壓DP,記憶化搜尋)

ccpc威海 D-Sternhalma(狀壓DP,記憶化搜尋)

題意

  • 給定六邊形棋盤每個格子的分數,詢問若干初始的棋子擺放
    方式,問按照規則移除棋子最多得多少分。
  • 移除棋子有兩種方式,一種是直接移除一個棋子,不得分;
    另一種是用一個棋子跳過其相鄰棋子,移除被跳過的棋子並
    且得分增加被移除棋子所在的格子的分數。

原題連結
解題思路
棋盤上的每個位置只有放與不放兩種選擇,故最終一共有\(2^{19}\)種狀態。初態已知,那麼我們可以用DP推出後面的所有狀態,也可以用記憶化搜尋。
程式碼細節較多,容易出錯。

DP

#include <bits/stdc++.h>
using namespace std;
const int N = (1 << 19) + 1;
//方向矩陣
const int d1[6][2] = {{0, 2}, {-1, 1}, {-1, -1}, {0, -2}, {1, -1}, {1, 1}};
const int d2[6][2] = {{0, 4}, {-2, 2}, {-2, -2}, {0, -4}, {2, -2}, {2, 2}};
//為每個點給定二維座標
const int coor[19][2] = 
{
    {1, 3}, {1, 5}, {1, 7},
    {2 ,2}, {2 ,4}, {2 ,6}, {2, 8},
    {3, 1}, {3, 3}, {3, 5}, {3, 7}, {3, 9},
    {4, 2}, {4 ,4}, {4 ,6}, {4 ,8},
    {5, 3}, {5 ,5}, {5 ,7}
};
int s[6][10];//點的權值
int id[8][15];//每個區域的編號
int f[N];
//將字串轉化為十進位制的狀態
int trans(string &mp)
{
    int ans = 0;
    for (int i = 0; i < 19; ++i)
    {
        if (mp[i] == '#')
            ans += (1 << i);
    }
    return ans;
}
int count(int x)
{
    int ans = 0;
    for (int i = 0; i < 19; ++i)
    {
        if (x & 1 << i)
            ++ans;
    }
    return ans;
}
vector<int> b;
void ini()
{
    for (int i = 0; i < (1 << 19); ++i)
        b.push_back(i);
    //排序,從小狀態推大狀態
    sort(b.begin(), b.end(), [](int a, int b)
    {
        return count(a) < count(b);
    });
    for (int i = 1; i < (1 << 19); ++i)
    {   
        int state = b[i];
        for (int j = 0; j < 19; ++j)
        {
            if (state & (1 << j))
            {
                int x = coor[j][0], y = coor[j][1];
                f[state] = max(f[state], f[state - (1 << j)]);
                for (int k = 0; k < 6; ++k)
                {
                    int x1 = x + d1[k][0], y1 = y + d1[k][1];
                    int x2 = x + d2[k][0], y2 = y + d2[k][1];
                    if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0)
                        continue;
                    if (id[x1][y1] == -1 || id[x2][y2] == -1)
                        continue;
                    //注意&與==的優先順序
                    if ((state & 1 << id[x1][y1]) == 0 || (state & 1 << id[x2][y2]) == 1)
                        continue;
                    f[state] = max(f[state], f[state ^ (1 << id[x][y]) ^ (1 << id[x1][y1]) ^ (1 << id[x2][y2])] + s[x1][y1]);
                }
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    memset(id, -1, sizeof(id));
    memset(f, -0x3f, sizeof(f));
    f[0] = 0;
    for (int i = 0; i < 19; ++i)
    {
        int x = coor[i][0], y = coor[i][1];
        id[x][y] = i;
        cin >> s[x][y];
    }
    ini();
    int n;
    cin >> n;
    while (n--)
    {
        string mp, t;
        for (int i = 0; i < 5; ++i)
            cin >> t, mp += t;
        cout << f[trans(mp)] << endl;;
    }
}

記憶化搜尋

#include <bits/stdc++.h>
using namespace std;
const int N = (1 << 19) + 1;
const int d1[6][2] = {{0, 2}, {-1, 1}, {-1, -1}, {0, -2}, {1, -1}, {1, 1}};
const int d2[6][2] = {{0, 4}, {-2, 2}, {-2, -2}, {0, -4}, {2, -2}, {2, 2}};
 
const int coor[19][2] = 
{
    {1, 3}, {1, 5}, {1, 7},
    {2 ,2}, {2 ,4}, {2 ,6}, {2, 8},
    {3, 1}, {3, 3}, {3, 5}, {3, 7}, {3, 9},
    {4, 2}, {4 ,4}, {4 ,6}, {4 ,8},
    {5, 3}, {5 ,5}, {5 ,7}
};
int s[6][10];//點的權值
int id[8][15];//每個區域的編號
int f[N];
int trans(string &mp)
{
    int ans = 0;
    for (int i = 0; i < 19; ++i)
    {
        if (mp[i] == '#')
            ans += (1 << i);
    }
    return ans;
}
int dfs(int state)
{
    if (f[state] != int(0xc1c1c1c1))
        return f[state];
    int &val = f[state];
    int grid[8][15] = {0};
    for (int i = 0; i < 19; ++i)
    {
        if (state & 1 << i)
        {
            int x = coor[i][0], y = coor[i][1];
            int n_state = state & ~(1 << i);
            grid[x][y] = 1;
            val = max(val, dfs(n_state));
        }        
    }
    
    for (int i = 0; i < 19; ++i)
    {
        if (state & 1 << i)
        {
            int x = coor[i][0], y = coor[i][1];
            for (int j = 0; j < 6; ++j)
            {
                int x1 = x + d1[j][0], y1 = y + d1[j][1];
                int x2 = x + d2[j][0], y2 = y + d2[j][1];
                if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0)
                    continue;
                if (!~id[x1][y1] || !~id[x2][y2])
                    continue;
                if (!grid[x1][y1] || grid[x2][y2])
                    continue;
                int n_state = state;
                n_state &= ~(1 << i);
                n_state &= ~(1 << id[x1][y1]);
                n_state |= (1 << id[x2][y2]);
                val = max(val, dfs(n_state) + s[x1][y1]);
            }
        }
    }
    return val;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    memset(id, -1, sizeof(id));
    memset(f, -0x3f, sizeof(f));
    f[0] = 0;
    for (int i = 0; i < 19; ++i)
    {
        int x = coor[i][0], y = coor[i][1];
        id[x][y] = i;
        cin >> s[x][y];
    }
    int n;
    cin >> n;
    while (n--)
    {
        string mp, t;
        for (int i = 0; i < 5; ++i)
            cin >> t, mp += t;
        cout << dfs(trans(mp)) << endl;;
    }
}