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 1∼N
商店里销售 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 1≤N≤12
- 1 ≤ M ≤ 1 0 3 1\\leq M\\leq10^3 1≤M≤103
- 1 ≤ a i ≤ 1 0 5 1\\leq a_i\\leq 10^5 1≤ai≤105
- 1 ≤ b i ≤ N 1\\leq b_i\\leq N 1≤bi≤N
- 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 1≤ci1<ci2<...<cibi≤N
- 所有输入的数都是整数
输出描述
输出一行一个正整数,代表解锁所有的箱子至少花费的金额。若无解输出-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 i−1个宝箱是否被开启。
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(=nowState∣thisState),需要金钱是现在的 金 钱 + 钥 匙 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集训队每周程序设计竞赛(13)题解
2021-2022-2 ACM集训队每周程序设计竞赛(13)题解
2021-2022-2 ACM集训队每周程序设计竞赛(13)题解