题意:共有 N 道菜,每道菜有一个满意值,小明要吃 M 道菜,其中有 K 种组合可以让小明连续吃下他们后获得额外的满意值,求最大满意值。
分析:因为菜的数量只有18,而且要考虑先后连续的顺序和吃 M 道菜停止 ,就要想到要明确表示出小明吃菜的状态,就可以用状压DP来做。状态用总的状态(二进制的state )和正在吃的菜( i )表示。
状态转移方程:dp[sta | tmp2][ j ] = max(dp[sta | tmp2][ j ], dp[sta] [i ] + com[ i ] [ j ] + a[ j ])
注意,i 从 1 扫到 N,但吃的菜不是(1<<i)而是 ( 1<<( i -1 ) )。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn = 19; int n, m, k; ll a[maxn]; ll dp[1 << maxn][19]; ll com[maxn][maxn]; int main() { scanf("%d%d%d", &n, &m, &k); for (int i = 1; i <= n; i++) { scanf("%d", a + i); } int x, y, z; for (int i = 1; i <= k; i++) { scanf("%d%d%d", &x, &y, &z); com[x][y] = z; } for (int i = 1; i <= n; i++) { dp[1 << (i - 1)][i] = a[i]; } int tot = (1 << n); int sta; ll ans = 0; for (int sta = 0; sta < tot; sta++) { // if(dp[sta]==0) int cnt = 0; for (int i = 1; i <= n; i++) { int tmp1 = 1 << (i - 1); // printf("sta=%d ", sta); if (tmp1 & sta) { cnt++; for (int j = 1; j <= n; j++) { if (j == i) continue; int tmp2 = 1 << (j - 1); if (!(sta & tmp2)) { // cnt++; ` // dp[sta|tmp2]+=a[j]; dp[sta | tmp2][j] = max(dp[sta | tmp2][j], dp[sta][i] + com[i][j] + a[j]); // printf("com%d %d=%lld ",i,j,com[i][j]); // ans = max(ans, dp[sta | tmp2]); } } } } // printf("yes cnt=%d ",cnt); if (cnt == m) { for (int i = 1; i <= n; i++) { if ( sta & (1 << (i - 1)) ) { ans = max(ans, dp[sta][i]); } } } } printf("%lld ", ans); }