HDU 4049 Tourism Planning(状压DP)题解
题意:m个城市,n个人,让这n个人按固定顺序走遍m个城市。每个城市有一个单人票价pi。每个人在每个城市能获得vij的价值。如果多个人在同一城市,那么会额外获得价值,给出一张n * n价值表,额外价值为任意两个组成队伍的价值和。每个人可以在中途退出,但是退出后不能再回来。问终点后最大价值。
思路:dp[st][i]表示在i城市状态st的最大价值,然后打表打出st的所有子集,每次都往子集转移。
代码:
#include<set> #include<map> #include<cmath> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 10 + 5; const int M = maxn * 30; const ull seed = 131; const int INF = 0x3f3f3f3f; const int MOD = 1e4 + 7; int dp[1 << maxn][maxn]; int p[maxn], v[maxn][maxn], b[maxn][maxn]; int eachother[1 << maxn], one[1 << maxn]; int nex[1 << 10][1 << 10 + 5], cnt[1 << 10]; int n, m; int main(){ memset(cnt, 0, sizeof(cnt)); for(int i = 0; i < (1 << 10); i++){ for(int j = 0; j <= i; j++){ if((i & j) == j) nex[i][cnt[i]++] = j; } } while(~scanf("%d%d", &n, &m) && n + m){ for(int i = 0; i < m; i++) scanf("%d", &p[i]); for(int i = 0; i < n; i++) for(int j = 0; 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]); memset(eachother, 0, sizeof(eachother)); for(int i = 0; i < (1 << n); i++){ for(int j = 0; j < n; j++){ if(!((1 << j) & i)) continue; for(int k = j + 1; k < n; k++){ if(!((1 << k) & i)) continue; eachother[i] += b[j][k]; } } } memset(dp, -INF, sizeof(dp)); for(int i = 0; i < (1 << n); i++){ dp[i][0] = 0; for(int j = 0; j < n; j++){ if((1 << j) & i) dp[i][0] += v[j][0] - p[0]; } dp[i][0] += eachother[i]; } for(int i = 0; i < m - 1; i++){ for(int j = 0; j < (1 << n); j++){ for(int k = 0; k < cnt[j]; k++){ int st = nex[j][k]; int now = dp[j][i]; for(int f = 0; f < n; f++){ if((1 << f) & st) now += v[f][i + 1] - p[i + 1]; } now += eachother[st]; dp[st][i + 1] = max(dp[st][i + 1], now); } } } int ans = 0; for(int i = 0; i < (1 << n); i++){ if(ans < dp[i][m - 1]){ ans = dp[i][m - 1]; } } if(ans > 0) printf("%d\n", ans); else printf("STAY HOME\n"); } return 0; }