题目大意:有n人,m个景点,每个景点有一个花费,每个人对每个景点有一个喜爱值,若去某个景点则每个人的bonus为对该景点的喜爱值减去该景点的花费,若两个人同时到某个景点则总bonus加上一个额外值,两两到同一点的额外值通过一个n*n的矩阵表示,每个人可以在中途离开,一旦离开不得再回来,现在旅行路线已经确定,求怎样计划每个人的去留使得总的bonus最大,输出最大bonus,若最大bonus小于等于0,则输出STAY HOME
题目分析:状态压缩dp,dp[i][s]表示经过前i个城市时,状态为s的总bonus的值,状态s表示n个人在或不在的状态,比如dp[3][9]表示经过前三个城市时第一和第四个人还在的状态(9 == 1001),c[i][s]表示经过第i个城市,状态为s的bonus值,先预处理出来c数组,状态转移相当于是通过人数转移(必然是从人多的状态转移到人少的状态,因为离开就不能再回来)所以这里有两种写法(第二种比较快),详细见程序注释
#include <bits/stdc++.h>
using namespace std;
int const INF = 0x3fffffff;
int n, m;
int dp[11][(1 << 11)], c[11][(1 << 11)];
//dp[i][s]表示经过前i个城市时,状态为s的总bonus的值
//c[i][s]表示经过第i个城市,状态为s的bonus值
int v[11][11], b[11][11], p[11];
void cal()
{
for(int i = 1; i <= m; i++) //景点数在外边;
{
for(int s = 0; s < (1 << n); s++) //2^n种状态
{
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()
{
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]); //同一个地点多个人去的喜爱度额外值
cal(); //计算每个城市每种状态下的值
//初始化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))//把最靠右的第一个1去掉;
{
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);
}
}