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