[hdu4940]Tourism Planning 状态压缩DP

本文介绍了一种使用状态压缩动态规划解决旅行计划优化问题的方法。该算法通过计算不同人员组合访问各个地点时的兴趣值、成本及额外奖励来决定最佳旅行方案。如果最终结果大于零,则输出最大收益值;否则建议待在家里。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

Several friends are planning to take tourism during the next holiday. They have selected some places to visit. They have decided which place to start their tourism and in which order to visit these places. However, anyone can leave halfway during the tourism and will never back to the tourism again if he or she is not interested in the following places. And anyone can choose not to attend the tourism if he or she is not interested in any of the places.
Each place they visited will cost every person certain amount of money. And each person has a positive value for each place, representing his or her interest in this place. To make things more complicated, if two friends visited a place together, they will get a non negative bonus because they enjoyed each other’s companion. If more than two friends visited a place together, the total bonus will be the sum of each pair of friends’ bonuses.
Your task is to decide which people should take the tourism and when each of them should leave so that the sum of the interest plus the sum of the bonuses minus the total costs is the largest. If you can’t find a plan that have a result larger than 0, just tell them to STAY HOME.

算法思路

  1. 其实状态压缩的想法是很简单的,因为奖励值需要我们知道在具体的状态之下到底存在哪些人,所以我们定义dp[i][j]为人员状态为j的时候到达i地点所可以得到的最优值.在每一步当中,都会离开一些人,人数未知,但是离开的人不会再回来,所以,我们得到的动态规划状态转移方程如下所示:
    DP[i][j]=max(DP[i][t]+w),for every t that t&k=k

    2.但是,这里要注意初始化的问题,我们在进行初始化的时候要将值定义为无穷小,只有这样才不会失去后效性,因为在上一步算出来的最优值是存在可能是一个负数的。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;

#define MAXN 2050

const int INF = 1<<30;
int dp[11][MAXN];
int n,m;
int bonus[11][11];
int cost[11],interest[11][11];
int sumbonus[MAXN];
int sumpeople[MAXN];
int suminterest[11][MAXN];

void Solve()
{
    int i,j,k,t;
    int ans = 0;

    memset(sumbonus,0,sizeof(sumbonus));
    memset(sumpeople,0,sizeof(sumpeople));
    memset(suminterest,0,sizeof(suminterest));
    memset(dp,0,sizeof(dp));


    for(i=0;i<(1<<n);i++)
        dp[0][i] = -INF;

    for(i=0;i<(1<<n);i++){
        for(j=0;j<n;j++){
            if(i&(1<<j)){
                sumpeople[i]++;
                for(k=j+1;k<n;k++){
                    if(i&(1<<k))
                        sumbonus[i] += bonus[j][k];
                }
            }
        }
    }

    for(i=0;i<m;i++){
        for(j=0;j<(1<<n);j++){
            for(k=0;k<n;k++){
                if(j&(1<<k))
                    suminterest[i][j] += interest[k][i];
            }
        }
    }

    for(i=0;i<(1<<n);i++){
        dp[0][i] = suminterest[0][i]+sumbonus[i]-sumpeople[i]*cost[0];
    }

    for(i=1;i<m;i++){
        for(k=0;k<(1<<n);k++){
            dp[i][k] = dp[i-1][k];
            for(t=0;t<(1<<n);t++){
                if((t&k)==k && dp[i-1][k]>-INF)
                    dp[i][k] = max(dp[i][k],dp[i-1][t]+sumbonus[k]-sumpeople[k]*cost[i]+suminterest[i][k]);
            }
        }
    }

    for(i=0;i<(1<<n);i++)
        ans = max(ans,dp[m-1][i]);

    if(ans>0)printf("%d\n",ans);
    else printf("STAY HOME\n");
    return;
}

int main()
{
    freopen("input","r",stdin);
    int i,j;

    while(1){
        scanf("%d%d",&n,&m);
        if(!n&&!m)break;

        for(i=0;i<m;i++)
            scanf("%d",&cost[i]);

        for(i=0;i<n;i++){
            for(j=0;j<m;j++)
                scanf("%d",&interest[i][j]);
        }

        for(i=0;i<n;i++){
            for(j=0;j<n;j++)
                scanf("%d",&bonus[i][j]);
        }

        Solve();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值