Codeforces 1042B Vitamins
链接: codeforces.com/contest/104…
题目描述
Berland shop sells n kinds of juices. Each juice has its price ci . 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.
Input
The first line contains a single integer n (1≤n≤1000) — the number of juices.
Each of the next n lines contains an integer ci (1≤ci≤100000) and a string si — the price of the i -th juice and the vitamins it contains. String si 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 si . The order of letters in strings si is arbitrary.
Output
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.
思路
- 状态压缩和背包, 将三种状态压缩为一个状态, 用一个三位二进制数表示三种状态.
- 只包含
A
的状态为001
, 只包含B
的状态为010
, 只包含C
的状态为100
. - 三种状态都包含的状态为
111
, 两种状态都包含的状态为011
,101
,110
. - 三种状态都不包含的状态为
000
.
- 只包含
- 用一个
dp
数组表示8种状态,dp[i]
表示状态为i
时的最小花费, 初始值为0x3f3f3f3f
. - 遍历每个果汁的价格数组
prices
- 对于每个果汁, 枚举八种状态(因为是三位二进制数), 将当前果汁的vitamin含量和枚举的状态取并集代表新的状态,
- 如果新的状态的花费大于当前状态加上当前果汁的价格, 则更新新状态的花费为当前状态加上当前果汁的价格.
- 最后返回
dp[7]
即可.
代码
public class Vitamins {
static Scanner scanner = new Scanner(new BufferedInputStream(System.in));
static int n;
static int[] dp = new int[8];
static int[] vitamins, prices;
public static void main(String[] args) {
n = scanner.nextInt();
prices = new int[n + 5];
vitamins = new int[n + 5];
Arrays.fill(dp, 0x3f3f3f3f); // 0x3f3f3f3f = 1061109567
dp[0] = 0; // 000, 代表如果没有果汁, 那么花费为0
for (int i = 1; i <= n; i++) { // 从第一个果汁开始
prices[i] = scanner.nextInt();
for (char c : scanner.next().toCharArray()) { // 枚举每个果汁的vitamin
switch (c) {
case 'A': // 001
vitamins[i] |= 1;
break;
case 'B': // 010
vitamins[i] |= 2;
break;
case 'C': // 100
vitamins[i] |= 4;
break;
}
}
}
for (int i = 1; i <= n; i++) // 遍历每个果汁
for (int j = 0; j <= 7; j++) { // 枚举八种状态
// 如果新的状态的花费大于当前状态加上当前果汁的价格, 则更新新状态的花费为当前状态加上当前果汁的价格
dp[j | vitamins[i]] = Math.min(dp[j | vitamins[i]], dp[j] + prices[i]);
}
// 如果dp[7] == 0x3f3f3f3f, 说明没有果汁能够满足Petya的需求, 输出-1
// 否则 输出dp[7]
System.out.println(dp[7] == 0x3f3f3f3f ? -1 : dp[7]);
}
}
LeetCode 148. Sort List
题目描述
一个单向链表, 依据结点的值对其进行从小到大排序, 要求时间复杂度为O(nlogn), 空间复杂度为O(1)
图例:
思路
- 采用归并排序的思想, 将链表分成两部分, 分别对两部分进行排序, 然后将两部分合并
- 首先, 采用快慢指针的方法, 找到链表的中间结点
- 然后, 递归的对两部分进行排序, 第一部分是头结点到中间结点的前一个结点, 第二部分是中间结点到尾结点
- 最后, 将两部分合并, 合并的方法是, 依次比较两部分的结点值, 将较小的结点插入到新链表中, 直到其中一部分的结点全部插入到新链表中, 然后将另一部分的结点全部插入到新链表中
- 递归的终止条件是, 当链表为空或者链表只有一个结点时, 不需要排序, 直接返回链表即可
代码
type ListNode struct {
Val int
Next *ListNode
}
func sortList(head *ListNode) *ListNode {
if head == nil || head.Next == nil { // 递归的终止条件
return head
}
slow, fast := head, head
var mid *ListNode
for fast != nil && fast.Next != nil { // 找到链表的中间结点
mid = slow
slow = slow.Next
fast = fast.Next.Next
}
mid.Next = nil // 将第一个链表断开
list1 := sortList(head) // 递归的对两部分进行排序
list2 := sortList(slow)
return merge(list1, list2) // 合并两个有序链表
}
func merge(list1, list2 *ListNode) *ListNode {
if list1 == nil { // 合并两个有序链表
return list2
}
if list2 == nil { // 合并两个有序链表
return list1
}
head := &ListNode{} // 新链表的头结点
rear := head // 新链表的尾结点
p1, p2 := list1, list2
for ; p1 != nil && p2 != nil; rear = rear.Next {
if p1.Val < p2.Val { // 依次比较两部分的结点值, 将较小的结点插入到新链表中
rear.Next = p1
p1 = p1.Next
} else if p1.Val >= p2.Val {
rear.Next = p2
p2 = p2.Next
}
}
if p1 != nil { // 将剩余的结点插入到新链表中
rear.Next = p1
}
if p2 != nil {
rear.Next = p2
}
return head.Next // 返回新链表的头结点的下一个结点
}
nil {
rear.Next = p2
}
return head.Next // 返回新链表的头结点的下一个结点
}