2021-2022-2 ACM集训队每周程序设计竞赛 - 问题 E: 宝藏开箱者 - 题解

Posted Tisfy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-2022-2 ACM集训队每周程序设计竞赛 - 问题 E: 宝藏开箱者 - 题解相关的知识,希望对你有一定的参考价值。

传送门


宝藏开箱者

时间限制:1秒
空间限制:128M


题目描述

N N N 个锁着的宝箱,编号分别是 1 ∼ N 1\\sim N 1N

商店里销售 M M M 种钥匙,第 i i i 种钥匙售价 a i a_i ai ¥,能解锁 b i b_i bi 个宝箱。能解锁的宝箱编号分别是: c i 1 , c i 1 , . . . , c i b i c_i_1, c_i_1, ..., c_i_b_i ci1,ci1,...,cibi。每把钥匙能用任意次数。

那么请问要解锁这所有的 N N N 个宝箱,买钥匙最少花费多少钱呢? 如果买完所有的钥匙都不能打开所有的宝箱,就输出-1。


输入描述

N M
a1 b1
c11 c12 ... c1b1
:
aM bM
cM1 cM2 ... cMbM

数据范围:

  • 1 ≤ N ≤ 12 1\\leq N\\leq 12 1N12
  • 1 ≤ M ≤ 1 0 3 1\\leq M\\leq10^3 1M103
  • 1 ≤ a i ≤ 1 0 5 1\\leq a_i\\leq 10^5 1ai105
  • 1 ≤ b i ≤ N 1\\leq b_i\\leq N 1biN
  • 1 ≤ c i 1 < c i 2 < . . . < c i b i ≤ N 1\\leq c_i_1<c_i_2<...<c_i_b_i\\leq N 1ci1<ci2<...<cibiN
  • 所有输入的数都是整数

输出描述

输出一行一个正整数,代表解锁所有的箱子至少花费的金额。若无解输出-1。


样例一

输入

2 3
10 1
1
15 1
2
30 2
1 2

输出

25

可以只买第1个和第3个钥匙,花费25元

样例二

输入

12 1
100000 1
2

输出

-1

样例三

输入

4 6
67786 3
1 3 4
3497 1
2
44908 3
2 3 4
2156 3
2 3 4
26230 1
2
86918 1
3

输出

69942

题目分析

本题可以使用状态压缩的动态规划。

一个“状态”(数字)的二进制的第 i i i位代表第 i − 1 i-1 i1个宝箱是否被开启。

d p [ s t a t e ] dp[state] dp[state]代表达到状态 s t a t e state state至少需要花费多少钱。

第一层枚举 m m m个钥匙。对于钥匙i,我们可以计算出钥匙i的“状态”码 t h i s S t a t e thisState thisState,然后从 0 0 0 1 < < n 1<<n 1<<n枚举所有现有的“状态” n o w S t a t e nowState nowState,如果购买钥匙i就能达到状态 t o S t a t e ( = n o w S t a t e ∣ t h i s S t a t e ) toState(=nowState|thisState) toState(=nowStatethisState),需要金钱是现在的 金 钱 + 钥 匙 i 的 售 价 ( d p [ n o w S t a t e ] + i t e m [ i ] ) 金钱+钥匙i的售价(dp[nowState]+item[i]) +i(dp[nowState]+item[i])

状态转移方程: d p [ t o S t a t e ] = m i n ( d p [ t o S t a t e ] , d p [ n o w S t a t e ] + m o n e y [ i t e m ] ) dp[toState] = min(dp[toState], dp[nowState] + money[item]) dp[toState]=min(dp[toState],dp[nowState]+money[item]);

初始状态 d p [ 0 ] = 0 dp[0]=0 dp[0]=0(什么宝箱都不解锁需要花费的钥匙钱为 0 0 0


AC代码

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;

int dp[1 << 12];
vector<int> keys[1000];
int money[1000];

int main() 
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < (1 << n); i++)
        dp[i] = 1e9;
    dp[0] = 0;
    for (int i = 0; i < m; i++) 
        int a, b;
        scanf ("%d%d", &a, &b);
        money[i] = a;
        for (int j = 0; j < b; j++) 
            int t;
            scanf("%d", &t);
            t--;  // 把输入的宝箱号[1,n]映射成方便状态压缩的[0,n-1]
            keys[i].push_back(t);
        
    
    for (int item = 0; item < m; item++) 
        int thisState = 0;
        for (int t : keys[item]) 
            thisState |= (1 << t);
        
        for (int nowState = 0; nowState < (1 << n); nowState++) 
            int toState = nowState | thisState;
            dp[toState] = min(dp[toState], dp[nowState] + money[item]);
        
    
    cout << (dp[(1 << n) - 1] == 1e9 ? -1 : dp[(1 << n) - 1]) << endl;
    return 0;

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/123415471

以上是关于2021-2022-2 ACM集训队每周程序设计竞赛 - 问题 E: 宝藏开箱者 - 题解的主要内容,如果未能解决你的问题,请参考以下文章

2021-2022-2 ACM集训队每周程序设计竞赛题解

2021-2022-2 ACM集训队每周程序设计竞赛(13)题解

2021-2022-2 ACM集训队每周程序设计竞赛(13)题解

2021-2022-2 ACM集训队每周程序设计竞赛(13)题解

2021-2022-2 ACM集训队每周程序设计竞赛 - 问题 C:回到学校 - 题解

2021-2022-2 ACM集训队每周程序设计竞赛(10) - 问题 C: 下一个素数 - 题解