Vitamins

题面翻译

数据有$n$组数,每组数有一个价值$c_i$和一个字符串S,字符串S中包含3个字母A,B,C,问集齐ABC三个字母的最小价值(一个字母可以有多个)

题目描述

Berland shop sells $ n $ kinds of juices. Each juice has its price $ c_i $ . Each juice includes some set of vitamins in it. There are three types of vitamins: vitamin “A”, vitamin “B” and vitamin “C”. Each juice can contain one, two or all three types of vitamins in it.

Petya knows that he needs all three types of vitamins to stay healthy. What is the minimum total price of juices that Petya has to buy to obtain all three vitamins? Petya obtains some vitamin if he buys at least one juice containing it and drinks it.

输入格式

The first line contains a single integer $ n $ $ (1 \le n \le 1,000) $ — the number of juices.

Each of the next $ n $ lines contains an integer $ c_i $ $ (1 \le c_i \le 100,000) $ and a string $ s_i $ — the price of the $ i $ -th juice and the vitamins it contains. String $ s_i $ contains from $ 1 $ to $ 3 $ characters, and the only possible characters are “A”, “B” and “C”. It is guaranteed that each letter appears no more than once in each string $ s_i $ . The order of letters in strings $ s_i $ is arbitrary.

输出格式

Print -1 if there is no way to obtain all three vitamins. Otherwise print the minimum total price of juices that Petya has to buy to obtain all three vitamins.

样例 #1

样例输入 #1

1
2
3
4
5
4
5 C
6 B
16 BAC
4 A

样例输出 #1

1
15

样例 #2

样例输入 #2

1
2
3
2
10 AB
15 BA

样例输出 #2

1
-1

样例 #3

样例输入 #3

1
2
3
4
5
6
5
10 A
9 BC
11 CA
4 A
5 B

样例输出 #3

1
13

样例 #4

样例输入 #4

1
2
3
4
5
6
7
6
100 A
355 BCA
150 BC
160 AC
180 B
190 CA

样例输出 #4

1
250

样例 #5

样例输入 #5

1
2
3
2
5 BA
11 CB

样例输出 #5

1
16

提示

In the first example Petya buys the first, the second and the fourth juice. He spends $ 5 + 6 + 4 = 15 $ and obtains all three vitamins. He can also buy just the third juice and obtain three vitamins, but its cost is $ 16 $ , which isn’t optimal.

In the second example Petya can’t obtain all three vitamins, as no juice contains vitamin “C”.

状态压缩DP

比赛时没想到用状态压缩DP,直接暴力了,今天看题解才发现dp是非常妙的。

状态压缩

用001表示A,010表示B,100表示C

DP

转移方程:

1
f[j|p[i]]=min(f[j|p[i]],f[j]+w[i]);

p[i]代表第i个字符串中含有的字母,比如:p[i]=011就代表第i个字符串含有AB。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n,w[N],p[N],f[8];
int main()
{
cin>>n;
char c;
memset(f,0x3f,sizeof f);
f[0]=0;
for(int i=0;i<n;i++)
{
cin>>w[i];
while(scanf("%c",&c)&&c!='\n')
{
if(c=='A')p[i]|=1;
else if(c=='B')p[i]|=2;
else if(c=='C')p[i]|=4;
}
}
for(int i=0;i<n;i++)
{
for(int j=7;j>=0;j--)
{
f[j|p[i]]=min(f[j|p[i]],f[j]+w[i]);
}
}
cout<<(f[7]==0x3f3f3f3f?-1:f[7])<<endl;
return 0;
}

暴力

昨天在场上直接暴力做了,就不具体解释暴力做法了,大家可以直接看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010;
typedef long long LL;
LL ans;
LL n;
LL min1[3];
LL min2[3]; // AB,AC,BC
LL min3 = 0x3f3f3f3f;
bool a = false, b = false, c = false;
bool find(string s, char x)
{
bool res = false;
for (int i = 0; i < s.size(); i++)
{
if (s[i] == x)
return true;
}
return false;
}
int main()
{
for (int i = 0; i < 3; i++)
{
min1[i] = min2[i] = 3e5;
}
cin >> n;

for (int i = 0; i < n; i++)
{
LL p;
string s;
cin >> p >> s;
sort(s.begin(), s.end());
if (find(s, 'A'))
a = true;
if (find(s, 'B'))
b = true;
if (find(s, 'C'))
c = true;
int l = s.size();
if (l == 1)
{
min1[s[0] - 'A'] = min(min1[s[0] - 'A'], p);
}
else if (l == 2)
{
if (s == "AB")
{
min2[0] = min(min2[0], p);
}
else if (s == "AC")
min2[1] = min(min2[1], p);
else
min2[2] = min(min2[2], p);
}
else
min3 = min(min3, p);
}
// cout<<a<<b<<c<<endl;
if (!a || !b || !c)
{
puts("-1");
return 0;
}

LL ans1 = 0, ans2 = 0, ans3 = 0;
LL ans4 = 0x3f3f3f3f;

ans3 = min3;

for (int i = 0; i < 3; i++)
{
ans1 += min1[i];
}
ans2 = min(min2[0] + min2[1], min2[1] + min2[2]);
ans2 = min(ans2, min2[0] + min2[2]);

for (int i = 0; i < 3; i++)
{
ans4 = min(min1[i] + min2[2 - i], ans4);
}
ans = min(ans1, ans2);
ans = min(ans, ans3);
ans = min(ans, ans4);
cout << ans << endl;
return 0;
}