题意: 有n人,m个景点,每个景点有一个花费,每个人对每个景点有一个喜爱值,若去某个景点则每个人的bonus为对该景点的喜爱值减去该景点的花费,若两个人同时到某个景点则总bonus加上一个额外值,两两到同一点的额外值通过一个n*n的矩阵表示,每个人可以在中途离开,一旦离开不得再回来,现在旅行路线已经确定,求怎样计划每个人的去留使得总的bonus最大,输出最大bonus,若最大bonus小于等于0,则输出STAY HOME
思路: 转自
复杂度算的有点问题,可以利用程序计算一下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<bits/stdc++.h>
#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);
}
}