#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::lower_bound;
using std::map;
using std::min;
using std::multiset;
using std::ostream;
using std::pair;
using std::priority_queue;
using std::queue;
using std::set;
using std::string;
using std::unordered_map;
using std::unordered_set;
using std::vector;
using pii = pair<int, int>;
template <typename T_container, typename T = typename std::enable_if<
!std::is_same<T_container, string>::value,
typename T_container::value_type>::type>
ostream &operator<<(ostream &os, const T_container &v);
template <typename A, typename B>
ostream &operator<<(std::ostream &os, const pair<A, B> &p) {
return os << '(' << p.first << ", " << p.second << ')';
}
template <typename T_container, typename T>
ostream &operator<<(ostream &os, const T_container &v) {
os << '{';
string sep;
for (const T &x : v) os << sep << x, sep = ", ";
return os << '}';
}
void dbg_out() { cerr << endl; }
template <typename Head, typename... Tail>
void dbg_out(Head H, Tail... T) {
cerr << ' ' << H;
dbg_out(T...);
}
#define LOCAL
#ifdef LOCAL
#define dbg(...) cerr << "(" << #__VA_ARGS__ << "):", dbg_out(__VA_ARGS__)
#else
#define dbg(...)
#endif
struct Edge {
int end;
long long weight;
bool isRoad;
};
class Comp {
public:
bool operator()(const Edge &e1, const Edge &e2) {
return e1.weight > e2.weight;
}
};
void dij(vector<vector<Edge>> &g, vector<long long> &shortestDist,
vector<bool> &reachedMinViaRoad) {
// yield the edge with the lowest weight
priority_queue<Edge, vector<Edge>, Comp> pq;
pq.push({1, 0, false});
while (!pq.empty()) {
Edge curEdge = pq.top();
pq.pop();
long long curWeight = curEdge.weight;
int curNode = curEdge.end;
if (shortestDist[curNode] != -1) {
if (curWeight > shortestDist[curNode]) {
continue;
} else if (curWeight == shortestDist[curNode]) {
reachedMinViaRoad[curNode] =
reachedMinViaRoad[curNode] || curEdge.isRoad;
continue;
} else {
__builtin_unreachable();
}
}
shortestDist[curNode] = curEdge.weight;
reachedMinViaRoad[curNode] = curEdge.isRoad;
for (Edge outEdge : g[curEdge.end]) {
outEdge.weight += curEdge.weight;
pq.push(outEdge);
}
}
}
void dij(vector<vector<Edge>> &g, vector<long long> &shortestDist,
vector<bool> &reachedMinViaRoad) {
priority_queue<Edge, vector<Edge>, Comp> pq;
pq.push({1, 0, false});
while (!pq.empty()) {
Edge curEdge = pq.top();
pq.pop();
// Dijkstra's algorithm guarantees that the first time we visit the node,
it
// will be the shortest path. Getting in this if statement would mean
that
// this is not the first time we are seeing this
if (shortestDist[curEdge.end] != -1) {
// if the current path is a longer path, discard
if (curEdge.weight > shortestDist[curEdge.end]) {
continue;
}
// if the current path has the same weight as a previously found path,
// check if we can use a road to reach this node
else if (curEdge.weight == shortestDist[curEdge.end]) {
reachedMinViaRoad[curEdge.end] =
reachedMinViaRoad[curEdge.end] || curEdge.isRoad;
// We already processed this node before, so continue
continue;
}
// Again: the algorithm guarantees that the first time we visit the
node
// will be the shortest path, so there is no way we can have a shorter
// path if we have visited a node before
else {
__builtin_unreachable();
}
}
shortestDist[curEdge.end] = curEdge.weight;
reachedMinViaRoad[curEdge.end] = curEdge.isRoad;
for (Edge outEdge : g[curEdge.end]) {
outEdge.weight += curEdge.weight;
pq.push(outEdge);
}
}
}
int main() {
cin.tie(0);
cout.tie(0);
int numCities, numRoads, numTrainRoutes;
cin >> numCities >> numRoads >> numTrainRoutes;
vector<vector<Edge>> g(numCities + 1);
for (int i = 0; i < numRoads; i++) {
int start, end, weight;
cin >> start >> end >> weight;
g[start].push_back({end, weight, true});
g[end].push_back({start, weight, true});
}
vector<Edge> allTrainRoutes;
for (int i = 0; i < numTrainRoutes; i++) {
int end, weight;
cin >> end >> weight;
g[1].push_back({end, weight, false});
g[end].push_back({1, weight, false});
allTrainRoutes.push_back({end, weight, false});
}
vector<long long> shortestDist(numCities + 1, -1);
vector<bool> reachedMinViaRoad(numCities + 1, false);
dij(g, shortestDist, reachedMinViaRoad);
int ans = 0;
vector<int> tot(numCities + 1, 0);
for (size_t i = 0; i < numTrainRoutes; i++) {
int end = allTrainRoutes[i].end;
long long routeWeight = allTrainRoutes[i].weight;
// if i was able to get to the city(
// from 1) with some weight less than the
// route's weight, I wanna remove this route
if (routeWeight > shortestDist[end]) {
ans++;
} else if (shortestDist[end] == routeWeight) {
if (reachedMinViaRoad[end]) {
ans++;
} else {
tot[end]++;
}
} else {
__builtin_unreachable();
}
}
for (int i = 1; i < numCities + 1; i++) {
if (tot[i]) {
ans += tot[i] - 1;
}
}
cout << ans << endl;
return 0;
}