HDU4049 Tourism Planning(状压dp 一个状态的所有子集)

题目

题意: 有n人,m个景点,每个景点有一个花费,每个人对每个景点有一个喜爱值,若去某个景点则每个人的bonus为对该景点的喜爱值减去该景点的花费,若两个人同时到某个景点则总bonus加上一个额外值,两两到同一点的额外值通过一个n*n的矩阵表示,每个人可以在中途离开,一旦离开不得再回来,现在旅行路线已经确定,求怎样计划每个人的去留使得总的bonus最大,输出最大bonus,若最大bonus小于等于0,则输出STAY HOME

思路: 转自
HDU4049 Tourism Planning(状压dp 一个状态的所有子集)_第1张图片
复杂度算的有点问题,可以利用程序计算一下n=10的时候 分解总次数。

for(int B=m;B;B=(B-1)&m)      //枚举m的非空子集{B},若是全部子集将判断提到循环末。
for(int B=m;B<=M;B=(B+1)|m)   //枚举m的父集{B},上界为M。
#include
#define low(x) ((x)&(-x))
using namespace std;
const int N=11,M=11,INF=0x3f3f3f3f;
int cost[M],like[N][M],val[N][N],bonus[1<<N],dp[M][1<<N],bin[1<<N];
int main(){
    for(int i=0;i<=10;++i) bin[1<<i]=i+1;
    int n,m,t;
    while(~scanf("%d%d",&n,&m),n||m){
        t=1<<n;
        for(int i=1;i<=m;++i) scanf("%d",&cost[i]);
        for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&like[i][j]);
        for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%d",&val[i][j]);
        for(int i=1;i<t;++i){
            bonus[i]=bonus[i^low(i)];//****
            for(int j=i^low(i);j;j^=low(j)) bonus[i]+=val[bin[low(i)]][bin[low(j)]];
        }
        memset(dp,-INF,sizeof dp),dp[0][t-1]=0;
        for(int i=0;i<m;++i)
            for(int s=0;s<t;++s)
                for(int b=s;;b=(b-1)&s){
                    int sum=bonus[b];
                    for(int p=b;p;p^=low(p)) sum+=like[bin[low(p)]][i+1]-cost[i+1];
                    dp[i+1][b]=max(dp[i+1][b],dp[i][s]+sum);
                    if(!b) break;//一定不可以写在21行for循环里 因为b=0时也应该被更新,代表i+1地点是所有游客离开。
                }
        int ans=0;
        for(int i=0;i<t;++i) ans=max(ans,dp[m][i]);
        if(ans<=0) puts("STAY HOME");
        else printf("%d\n",ans);
    }
}

你可能感兴趣的:(DP==状压dp)