CF580D Kefa and Dishes [洛谷]

题目传送门

一、题目大意

kefa进入了一家餐厅,这家餐厅中有n个菜(0<n<=18),kefa对第i个菜的满意度为ai0<=ai<=109),并且对于这n个菜有k个规则,如果kefa在吃完第xi个菜之后吃了第yi个菜(保证xiyi不相等),那么会额外获得ci0<=ci<=109)的满意度。kefa要吃m任意的菜(0<m<=n),但是他希望自己吃菜的顺序得到的满意度最大,请你帮帮kefa吧!

输入第一行是三个数:n,m,k

第二行是n个数,第i个数表示kefa对第i道菜的满意度为ai

第三行到第k+2行每行有3个数:xiyici,表示如果kefa在吃完第xi道菜之后立刻吃了第yi道菜,则会额外获得ci的满意度

输入#1

2 2 1
1 1
2 1 1

输出#1

3

输入 #2

4 3 2
1 2 3 4
2 1 5
3 4 2

输出 #2

12

二、题目分析

翻译的人还好心的告诉我们要用状压。。。,其实看看数据范围也可以证明这一点,n<=18。而且"XXX"最大之流,看着就像是动态规划吧!

这题在一般状压的基础上加了一点变化:状态之间有联系,其实也是废话,状态之间没有冲突,没有关联,还用你来做题吗?

题目中说:这n个菜有k个规则,如果kefa在吃完第xi个菜之后吃了第yi个菜(保证xiyi不相等),那么会额外获得ci (0<=ci<=109)的满意度。
显然这是与吃菜的顺序有关的

但是再仔细考虑一下,会发现其实也是没有后效性

因为一道菜只与它前面的那道菜有关

显然我们可以枚举要吃的菜的前面那道菜

不妨设状态f[i][j]表示当状态为i时,最后吃的一道菜为j时获得的最大满意度

  • 当前状态i : f[i]

  • 当前状态i包含j这道菜,且前序状态不包含j

因为i需要包含了j这道菜,即i的第j位置必须是1
前序状态不能包含j这道菜,即前序状态的第j位置需要是0
也就是:f[i^(1<<j)]

可行的状态有转移方程:
f[i][j]=max(f[i^(1<<j)][k]+A[j]+w[k][j])

显然当那些状态中存在m1时(已经吃了m道菜)取max即为最后的答案

三、完整代码

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

const int N = 20;
int a[N], c[N][N];
int K;
int n, m;
LL f[1 << N][N], ans;

//统计某个状态中数字1的数量
int count(int x) {
    int res = 0;
    for (int i = 0; i < 32; i++) res += x >> i & 1;
    return res;
}

int main() {
    cin >> n >> m >> K;
    for (int i = 0; i < n; i++) { //下标从0开始
        cin >> a[i];
        f[1 << i][i] = a[i];      //只吃一个的初始化
    }
    while (K--) {
        int x, y, w;
        cin >> x >> y >> w;
        x--, y--;                 //状态压缩的套路
        c[x][y] = w;              //记录x,y的前后关联关系
    }
    for (int i = 0; i < (1 << n); i++) {        //枚举所有状态
        for (int j = 0; j < n; j++) {           //枚举每一位
            if ((i & (1 << j)) == 0) continue;  //不包含j的不是本次要计算的状态
            for (int k = 0; k < n; k++) {       //枚举前一次最后一个菜k,看看会不会获取到更大的满意度
                /*
                1、本轮吃下j,最后形成i这样的状态,那么上轮没有j,状态为 i^(1<<j)
                2、上轮的状态是固定的,那么上轮的什么是不固定的呢?
                答案:最后吃的是什么不固定!我们需要枚举所有上轮最后一个菜吃的是什么东西。
                很显然,上轮最后一个菜不能是j。
                如果上轮最后一个菜是k,本轮还只能吃j,则本轮的结果中必然包含k!
                如果不包含k,那么k是天上掉下来的吗?这就是本题的状态之间关联关系的推导过程
                由此可见,状态压缩DP的精髓在于:
                状态的转移关系确定!
                */
                if (j == k || (!(i & (1 << k))))continue;//相同或没吃过k不合法
                //dp转移方程
                f[i][j] = max(f[i][j], f[i ^ (1 << j)][k] + a[j] + c[k][j]);
            }
            //已经吃了m个菜就统计答案
            if (m == count(i))ans = max(ans, f[i][j]);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
posted @   糖豆爸爸  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2018-02-10 大数据统计分析平台之三、Kibana安装和使用
2018-02-10 大数据统计分析平台之一、Kafka单机搭建
2012-02-10 在Linux下配置squid[转载,待测试]
2012-02-10 内网用ssh连接linux很慢
2012-02-10 centos5.5 64位提升sata硬盘速度
2012-02-10 CentOS 64bit密码正确却无法登录系统
2012-02-10 Linux的内存释放脚本
Live2D
点击右上角即可分享
微信分享提示