codeforces 580D 状压DP

codeforces 580D


题意:

饭 店 里 有 n 种 菜 , 吃 第 i 种 菜 带 来 a i 点 满 意 度 , 你 需 要 吃 m 种 菜 才 能 吃 饱 , 吃 的 菜 品 不 能 重 复 。 饭店里有n种菜,吃第i种菜带来a_i点满意度,你需要吃m种菜才能吃饱,吃的菜品不能重复。 niaim
给 出 k 组 x i 、 y i 、 c i , 表 示 先 吃 第 x i 种 菜 , 再 吃 第 y i 种 菜 , 可 以 额 外 带 来 c i 点 满 意 度 。 给出k组x_i、y_i、c_i,表示先吃第x_i种菜,再吃第y_i种菜,可以额外带来c_i点满意度。 kxiyicixiyici
问 吃 饱 后 的 最 大 满 意 度 。 问吃饱后的最大满意度。


题解:

d p [ i ] [ j ] 表 示 i 状 态 下 最 后 一 盘 菜 为 j 的 满 意 度 , 其 中 二 进 制 状 态 下 的 i 的 各 位 1 和 0 代 表 吃 与 不 吃 。 dp[i][j]表示i状态下最后一盘菜为j的满意度,其中二进制状态下的i的各位1和0代表吃与不吃。 dp[i][j]iji10

  • 若 二 进 制 状 态 下 的 i 的 第 j 位 等 于 1 , 而 第 k 位 等 于 0 , d p [ i ∣ ( 1 < < k − 1 ) ] [ k ] = m a x ( d p [ i ∣ ( 1 < < k − 1 ) ] [ k ] , d p [ i ] [ j ] + a [ k ] + v [ j ] [ k ] ) 若二进制状态下的i的第j位等于1,而第k位等于0,dp[i | (1<<k-1)][k] = max(dp[i | (1<<k-1)][k], dp[i][j]+a[k]+v[j][k]) ij1k0dp[i(1<<k1)][k]=max(dp[i(1<<k1)][k],dp[i][j]+a[k]+v[j][k])
  • 遍 历 1 < < n 种 状 态 , 若 二 进 制 状 态 下 的 i 的 各 位 之 和 等 于 m , 则 状 态 合 法 遍历1<<n种状态,若二进制状态下的i的各位之和等于m,则状态合法 1<<nim

#include 
using namespace std;
typedef long long ll;
ll a[20], v[20][20];
ll dp[1<<18][20];

int main() {
    int n, m, k, x, y;
    cin >> n >> m >> k;
    for(int i = 1 ; i <= n ; i++){
        cin >> a[i];
        dp[1<<(i-1)][i] = a[i];
    }
    ll c;
    for(int i = 1 ; i <= k ; i++){
        cin >> x >> y >> c;
        v[x][y] = c;
    }
    ll ans = 0;
    for(int i = 1 ; i < (1<<n) ; i++){
        for(int j = 1 ; j <= n ; j++){
            if(i & 1<<(j-1)){
                for(int k = 1 ; k <= n ; k++){
                    if(!(i & 1<<(k-1))){
                        dp[i | (1<<k-1)][k] = max(dp[i | (1<<k-1)][k], dp[i][j]+a[k]+v[j][k]);
                    }
                }
            }
        }
    }
    for(int i = 1 ; i < (1<<n) ; i++){
        if(__builtin_popcount(i) == m){
            for(int k = 1 ; k <= n ; k++){
                ans = max(ans, dp[i][k]);
            }
        }
    }
    cout << ans << endl;
    return 0; 
}

你可能感兴趣的:(状压DP)