1. 程式人生 > >HDU 4049 Tourism Planning(狀態壓縮DP)

HDU 4049 Tourism Planning(狀態壓縮DP)

http://acm.hdu.edu.cn/showproblem.php?pid=4049


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const INF = 0x3fffffff;
int n, m;
//dp[i][s]表示經過前i個城市時,狀態為s的總bonus的值,狀態s表示n個人在或不在的狀態
int dp[11][(1 << 11)], c[11][(1 << 11)];
int v[11][11], b[11][11], p[11];

void init() {
    for(int i = 1; i <= m; i++) { //景點
        for(int s = 0; s < (1 << n); s++) { //狀態
            c[i][s] = 0;    //初始化為0
            for(int j = 0; j < n; j++) {
                //經過前i個景點第j個人在的狀態
                if(s & (1 << j)) {
                    //值=喜愛程度-花費
                    c[i][s] += v[j][i] - p[i];
                    //若在第j個人在的狀態下,其前的第k個人也在
                    //則說明他們必然同時去了前i個景點,加上額外值
                    for(int k = 0; k < j; k++)
                        if(s & (1 << k))
                            c[i][s] += b[k][j];
                }
            }
        }
    }
}

int main() {
    freopen("data.in", "r", stdin);
    while(scanf("%d %d", &n, &m) != EOF && (n + m)) {
        for(int i = 1; i <= m; i++)
            scanf("%d", &p[i]);     //到每個景點的花費

        for(int i = 0; i < n; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &v[i][j]);  //每個人到對每個景點的喜愛程度

        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                scanf("%d", &b[i][j]);  //同一個地點多個人去的喜愛度額外值

        init();  //預處理,計算每個城市每種狀態下的值

        //初始化dp
        for(int i = 1; i <= m; i++)
            for(int s = 0; s < (1 << n); s++)
                dp[i][s] = -INF;
        int ans = -INF;

        // 寫法1:
        // for(int i = 1; i <= m; i++) //景點
        //     for(int s = 0; s < (1 << n); s++) //當前狀態s
        //         for(int j = (1 << n) - 1; j >= s; j--)  //s之後的所有狀態,因為人走了就不能回來
        //             if((j & s) == s)    //若當前狀態是之前某個狀態的子狀態
        //                 //相當於揹包,經過前i個城市,狀態為s的值可能由經過前i-1個城市狀態為j
        //                 //(j-> s:有人離開)的值加上經過第i個城市狀態為s的值
        //                 dp[i][s] = max(dp[i][s], dp[i - 1][j] + c[i][s]);

        // 寫法2:
        for(int i = 1; i <= m; i++)
            for(int s = 0; s < (1 << n); s++) {
                for(int j = s; ; j = ((j - 1) & s)) {//列舉子集
                    if(j == 0) { //向前列舉到0
                        dp[i][0] = max(dp[i][0], dp[i - 1][s] + c[i][0]);
                        break;
                    }
                    //列舉s的子狀態(s -> j有人離開),與寫法1正好相反
                    dp[i][j] = max(dp[i][j], dp[i - 1][s] + c[i][j]);

                }
            }

        //求遊覽m個景點下的最大值
        for(int s = 0; s < (1 << n); s++)
            ans = max(ans, dp[m][s]);
        if(ans <= 0)
            printf("STAY HOME\n");
        else
            printf("%d\n", ans);
    }
}