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);
    }
}

猜你喜欢

转载自blog.csdn.net/ccshijtgc/article/details/83021462
今日推荐