多多捕蝇
拼多多技术岗 7月3号笔试 第三题
题目内容
现在多多宿舍区有 nnn 个房间(编号 111 到 nnn)和一只苍蝇,多多准备通过在房间放置粘蝇板的方式进行捕蝇,如果要在房间 iii 捕捉到苍蝇,需要在该房间放置 CiC_iCi 个粘蝇板。
苍蝇会在各个房间进行活动,如果它现在在房间 iii,下一分钟它会跑到房间 aia_iai(如果 i=aii = a_ii=ai,表示苍蝇不会离开房间),苍蝇所在的初始房间是随机不确定的,它可能初始出现在任意一个房间。
如果需要捕捉到这只苍蝇,请你帮多多计算下至少需要多少个粘蝇板。
输入描述
每个测试用例,第一行包含一个整数 nnn(1≤n≤1051 \le n \le 10^51≤n≤105),表示房间的数量;
第二行包含 nnn 个整数 c1,c2,...,cnc_1,c_2,...,c_nc1,c2,...,cn(1≤ci≤1041 \le c_i \le 10^41≤ci≤104),cic_ici 表示在房间 iii 捕捉到苍蝇需要的粘蝇板数量;
第三行包含 nnn 个整数 a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an(1≤ai≤n1 \le a_i \le n1≤ai≤n),aia_iai 表示在当前房间 iii 的苍蝇,下一分钟去到的房间。
输出描述
每个测试用例输出一个整数,表示多多捕捉到苍蝇需要的最少的粘蝇板数量。
样例1
输入
4
1 10 2 10
2 4 2 2
输出
10
说明
把粘蝇板放在第 222 个房间,无论苍蝇最开始出现在哪个房间,在经过一段时间之后都会通过第 222 个房间,所需要的粘蝇板数量就是 101010。
题解
思路
解决思路: 拓扑排序 + DFS
- 这道题本质是一个有向图,这个图有个特点就是
所有节点要不属于某个环,要不指向一个环。 - 接下来考虑怎么放置成本最低,分情况讨论
- 位于环中,由于环中中节点全部可互相访问,一个环中最低成本就为
环中最小min(c) - 指向环的节点,指向环的节点根本无需放置,可由指向环中节点帮忙捕获。利用
A -> B -->C把C看作一个环,覆盖这个块最低成本就是选择C。
- 位于环中,由于环中中节点全部可互相访问,一个环中最低成本就为
- 根据2的分析,根本无需处理指向环的节点,这部分节点可通过
拓扑排序进行剔除。成本由环决定,可通过递归处理接下来每个环,总成本为所有环中sum(min(c))
C++
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n + 1);
vector<int> c(n + 1);
vector<int> indeg(n + 1, 0);
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= n; i++) {
cin >> a[i];
indeg[a[i]]++;
}
queue<int> q;
for (int i = 1; i <= n; i++) {
if (indeg[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
int v = a[u];
indeg[v]--;
if (indeg[v] == 0) {
q.push(v);
}
}
long ans = 0;
// 现在剩余的都是环
vector<int> vis(n + 1, false);
for (int i = 1; i <= n; i++) {
if (indeg[i] > 0 && !vis[i]) {
int current = i;
int minCost = c[i];
vis[i] = true;
current = a[current];
while (current != i) {
minCost = min(minCost, c[current]);
vis[current] = true;
current = a[current];
}
ans += minCost;
}
}
cout << ans;
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] a = new int[n + 1];
int[] c = new int[n + 1];
int[] indeg = new int[n + 1];
String[] parts = br.readLine().split(" ");
for (int i = 1; i <= n; i++) {
c[i] = Integer.parseInt(parts[i - 1]);
}
parts = br.readLine().split(" ");
for (int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(parts[i - 1]);
indeg[a[i]]++;
}
Queue<Integer> q = new LinkedList<>();
for (int i = 1; i <= n; i++) {
if (indeg[i] == 0) {
q.offer(i);
}
}
while (!q.isEmpty()) {
int u = q.poll();
int v = a[u];
indeg[v]--;
if (indeg[v] == 0) {
q.offer(v);
}
}
long ans = 0;
// 现在剩余的都是环
boolean[] vis = new boolean[n + 1];
for (int i = 1; i <= n; i++) {
if (indeg[i] > 0 && !vis[i]) {
int current = i;
int minCost = c[i];
vis[i] = true;
current = a[current];
while (current != i) {
minCost = Math.min(minCost, c[current]);
vis[current] = true;
current = a[current];
}
ans += minCost;
}
}
System.out.println(ans);
}
}
python
python
import sys
from collections import deque
data = list(map(int, sys.stdin.read().split()))
n = data[0]
idx = 1
a = [0] * (n + 1)
c = [0] * (n + 1)
indeg = [0] * (n + 1)
for i in range(1, n + 1):
c[i] = data[idx]
idx += 1
for i in range(1, n + 1):
a[i] = data[idx]
indeg[a[i]] += 1
idx += 1
q = deque()
for i in range(1, n + 1):
if indeg[i] == 0:
q.append(i)
while q:
u = q.popleft()
v = a[u]
indeg[v] -= 1
if indeg[v] == 0:
q.append(v)
ans = 0
# 现在剩余的都是环
vis = [False] * (n + 1)
for i in range(1, n + 1):
if indeg[i] > 0 and not vis[i]:
current = i
min_cost = c[i]
vis[i] = True
current = a[current]
while current != i:
min_cost = min(min_cost, c[current])
vis[current] = True
current = a[current]
ans += min_cost
print(ans)
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on("line", (line) => {
input.push(...line.trim().split(/\s+/));
});
rl.on("close", () => {
let idx = 0;
const n = Number(input[idx++]);
const a = new Array(n + 1).fill(0);
const c = new Array(n + 1).fill(0);
const indeg = new Array(n + 1).fill(0);
for (let i = 1; i <= n; i++) {
c[i] = Number(input[idx++]);
}
for (let i = 1; i <= n; i++) {
a[i] = Number(input[idx++]);
indeg[a[i]]++;
}
const q = [];
let head = 0;
for (let i = 1; i <= n; i++) {
if (indeg[i] === 0) {
q.push(i);
}
}
while (head < q.length) {
const u = q[head++];
const v = a[u];
indeg[v]--;
if (indeg[v] === 0) {
q.push(v);
}
}
let ans = 0;
// 现在剩余的都是环
const vis = new Array(n + 1).fill(false);
for (let i = 1; i <= n; i++) {
if (indeg[i] > 0 && !vis[i]) {
let current = i;
let minCost = c[i];
vis[i] = true;
current = a[current];
while (current !== i) {
minCost = Math.min(minCost, c[current]);
vis[current] = true;
current = a[current];
}
ans += minCost;
}
}
console.log(ans);
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
var n int
fmt.Fscan(in, &n)
a := make([]int, n+1)
c := make([]int, n+1)
indeg := make([]int, n+1)
for i := 1; i <= n; i++ {
fmt.Fscan(in, &c[i])
}
for i := 1; i <= n; i++ {
fmt.Fscan(in, &a[i])
indeg[a[i]]++
}
q := make([]int, 0)
head := 0
for i := 1; i <= n; i++ {
if indeg[i] == 0 {
q = append(q, i)
}
}
for head < len(q) {
u := q[head]
head++
v := a[u]
indeg[v]--
if indeg[v] == 0 {
q = append(q, v)
}
}
var ans int64 = 0
// 现在剩余的都是环
vis := make([]bool, n+1)
for i := 1; i <= n; i++ {
if indeg[i] > 0 && !vis[i] {
current := i
minCost := c[i]
vis[i] = true
current = a[current]
for current != i {
if c[current] < minCost {
minCost = c[current]
}
vis[current] = true
current = a[current]
}
ans += int64(minCost)
}
}
fmt.Println(ans)
}