/*
* Problem :: 2056 / 작업
*
* Kind :: Dynamic Programming + Data Structure
*
* Insight
* - 어떤 작업 x 가 끝났을 때, 그 x 를 선행 작업으로 가지고 있는 작업들에게
* 어떻게 x 라는 작업이 끝났음을 알려줄 수 있을까?
* + 굳이 x 라는 작업이 끝났음을 알려주어야 할까?
* 작업 y 가 있고 선행 작업으로 x 를 가지고 있다고 할 때,
* 작업 y 의 총 선행 작업들의 개수를 n 개라고 하면
* x 가 끝났을 때 이 n 을 감소시켜서 n 이 0 이 되었을 때 작업을 실행시켜주면 되지 않을까?
* # 작업을 실행시켰을 때 끝나는 시간까지 고려해주고
* 우선순위 큐를 활용해서 현재 실행중인 작업들 중 가장 빨리 끝나는 작업을 찾고
* 그 작업을 선행 작업으로 가지고 있는 작업들을 갱신시켜준다면... 풀수있겠다!
*
* Point
* - 1번 작업 외에도 선행 작업이 하나도 없는 작업이 있을 수 있다!
* + 총 선행 작업들의 개수가 0 개인 작업들을 동시에 실행시켜주자
*/
//
// BOJ
// ver.C++
//
// Created by GGlifer
//
// Open Source
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define endl '\n'
// Set up : Global Variables
struct Operation { int no, t; };
// Set up : Functions Declaration
bool operator < (const Operation &u, const Operation &v);
int main()
{
// Set up : I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Set up : Input
int N; cin >> N;
int T[N+1]; vector<int> K[N+1]; int C[N+1];
for (int i=1; i<=N; i++) {
cin >> T[i];
int n; cin >> n;
C[i] = n; /* i 번 작업의 총 선행 작업들의 개수는 n 개 */
for (int j=0; j<n; j++) {
int no; cin >> no;
K[no].push_back(i); /* no 번 작업은 i 번 작업을 선행 작업으로 가짐 */
}
}
// Process
priority_queue<Operation> pq;
for (int i=1; i<=N; i++) {
if (C[i] == 0) { /* 선행 작업이 하나도 없는 작업들을 우선순위 큐에 추가 */
pq.push({i, T[i]});
}
}
int ans = -1;
while (not(pq.empty())) {
auto [c, t] = pq.top(); /* 실행중인 작업들 중
* 가장 빨리 끝나는 작업의 번호(c)와 그 때 시간(t) */
pq.pop();
for (int n : K[c]) { /* c 번 작업을 선행 작업으로 가지고 있는 작업(n)들 순회 */
C[n]--; /* n 번 작업의 실행되어야 할 선행 작업들의 총 개수 감소 */
if (C[n] == 0) { /* 실행되어야 할 선행 작업들이 하나도 없다면 */
pq.push({n, t+T[n]}); /* 우선순위 큐에 추가 */
}
}
if (pq.empty()) {
ans = t; /* 마지막 작업이 끝나는 시간 */
}
}
// Control : Output
cout << ans << endl;
}
// Helper Functions
bool operator < (const Operation &u, const Operation &v)
/* Operation 구조체의 최소힙 정렬을 위한 연산자 오버로딩 */
{
return make_pair(u.t, u.no) > make_pair(v.t, v.no);
}