nyoj832 合併遊戲(狀態壓縮DP)

合併遊戲 題目鏈接
題意 : n個石子, 給你一個n*n矩陣, A[i][j]表示第i個和第j個合併蹦出的金幣值, 合併完石子j消失。求合併所有石子後,所得的最大金幣數。
分析 :
1、 題中給的數據範圍 n(1<=n<=10) 也就是說最多10個石子, 那麼我們不妨用一個二進制串來表示合併的狀態,1表示沒被合併,0表示合併後消失了,例如(1001)四個石子第2、3個被合併了。
2、 用d[x]來存儲合併到x狀態時,所得的最大金幣數,例如 d[1001] 表示合併2 , 3 後所得的最大金幣數。
3、 我們從開始狀態開始搜索(例如4個石子:1111), 枚舉合併1個石子後的所以狀態 (1110, 1101, 1011, 0111), 再繼續往下一狀態求, 1110下一狀態:(0110, 1010, 1100 ), 1101 —>(1100, 1001, 0101) , 1011 —> (1010, 1001, 0011) , 0111 —> (0110, 0101, 0011), 繼續往下一狀態求 ……….. 用遞歸不斷求下一狀態。
1110有3種合併方法, 可以由1111中的最後一石子與前三個石子任何一個合併而來。注意:細心會發現 1110, 1101, 1011, 0111 他們的下一狀態有重複的, 所以邊搜邊標記, 纔不會做多餘的工作。

#include<iostream>
#include<cstdio>
#include<string.h>
#include<math.h>
using namespace std;

int n, a[15][15], d[10000];
int dp(int x)
{
    if(d[x] != -1) return d[x];//搜過的狀態要標記, 這要注意!! 不寫的話會超時
    int Mx = 0;
    if(x == 0) return 0;
    for(int i = 0; i < n; i++)
    {
        int mx = 0;
        if(x & (1 << i))//枚舉所有可以合併的石子, 第i+1個
        {
            int tem = x - (1 << i);//合併完的狀態,
            for(int j = 0; j < n; j++)
            {
                if(tem & (1 << j))//枚舉所有可以與第i+1個石子合併的石子, 求出最大的
                    mx = max(a[1+j][i+1], mx);
            }
            Mx = max(Mx, dp(tem) + mx);
        }
    }
    d[x] = Mx;
    return d[x];
}
int main()
{
    while(scanf("%d", &n) != EOF)
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                scanf("%d", &a[i][j]);
        memset(d, -1, sizeof(d));
        int ans = dp((1 << n) - 1);
        printf("%d\n", ans);
    }
    return 0;
}
發佈了32 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章