#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
#include <unordered_map>
#include <map>
#include <algorithm>
class Seg {
std::ifstream fin;
std::ofstream fout;
int n;
struct Point {
double x, y;
double dist(Point p) {
return sqrt((p.x - x) * (p.x - x) + (p.y - y) * (p.y - y));
}
};
struct Line {
Point a, b;
};
std::vector <Line> lines;
std::vector <double> dp[2][17];
void reset() {
lines.clear();
for(int i = 0; i < 2; i ++) {
for(int j = 0;j < 17; j ++) {
dp[i][j].clear();
}
}
}
void readTest() {
fin >> n;
lines.resize(n);
for(int i = 0; i < n; i ++) {
double x1, x2, y1, y2;
fin >> x1 >> y1 >> x2 >> y2;
lines[i] = {{x1, y1}, {x2, y2}};
}
}
void solveTest() {
for(int i = 0; i < 2; i ++) {
// dp[i].resize(n);
for (int j = 0; j < n; j++) {
dp[i][j].resize(1 << n, 1e18);
}
}
dp[0][0][1] = 0;
for(int bitmask = 1; bitmask < (1<<n); bitmask ++) {
for(int endNode = 0; endNode < n; endNode ++) {
if(bitmask & (1<<endNode)) {
for(int x=0; x < n; x ++) {
if(x == endNode or (bitmask & (1<<x)) == 0){
continue;
}
int prevMask = bitmask ^ (1 << endNode);
dp[0][endNode][bitmask] = std::min(dp[0][endNode][bitmask], dp[0][x][prevMask] + lines[x].a.dist(lines[endNode].b)); //dist[0][1][x][endNode]
dp[0][endNode][bitmask] = std::min(dp[0][endNode][bitmask], dp[1][x][prevMask] + lines[x].b.dist(lines[endNode].b)); // dist[1][1][x][endNode]
dp[1][endNode][bitmask] = std::min(dp[1][endNode][bitmask], dp[0][x][prevMask] + lines[x].a.dist(lines[endNode].a)); // dist[0][0][x][endNode]
dp[1][endNode][bitmask] = std::min(dp[1][endNode][bitmask], dp[1][x][prevMask] + lines[x].b.dist(lines[endNode].a)); // dist[1][0][x][endNode]
}
}
}
}
double sol = 1e18;
for(int i = 1; i < n; i ++) {
sol = std::min(sol, dp[0][i][(1<<n) - 1] + lines[i].a.dist(lines[0].b));// dist[0][1][i][0]);
sol = std::min(sol, dp[1][i][(1<<n) - 1] + lines[i].b.dist(lines[0].b));// dist[1][1][i][0]);
}
fout << sol << '\n';
}
public:
void solve() {
fin.open("seg.in");
fout.open("seg.out");
int Q;
fin >> Q;
fout << std::setprecision(6) << std::fixed;
while(Q --) {
this->reset();
this->readTest();
this->solveTest();
}
fin.close();
fout.close();
}
};
// https://leetcode.com/problems/shortest-path-visiting-all-nodes/
class ShortestPath {
std::ifstream fin;
std::ofstream fout;
public:
int shortestPathLength(std::vector<std::vector<int>>& graph) {
int n = graph.size();
bool visited[(1<<n)][n];
for(int mask=0; mask < (1<<n); mask ++) {
for(int i = 0; i < n; i ++) {
visited[mask][i] = false;
}
}
std::queue<std::pair <int, int>> q, q2;
for(int i = 0; i < n; i ++) {
q.push({i, (1<<i)});
}
int dist = 0;
while(q.empty() == false) {
while(q.empty() == false) {
auto [node, mask] = q.front();
q.pop();
if(mask == (1<<n) - 1) {
return dist;
}
if(visited[mask][node]) {
continue;
}
visited[mask][node] = true;
for(int next: graph[node]) {
q2.push({next, mask | (1<< next)});
}
}
std::swap(q, q2);
dist += 1;
}
return -1;
}
void solve() {
fin.open("input.txt");
fout.open("output.txt");
int n;
fin >> n;
std::vector <std::vector <int>> graph(n);
for(int i = 0; i < n; i ++) {
int k;
fin >> k;
for(int j = 0; j < k; j ++) {
int temp;
fin >> temp;
graph[i].push_back(temp);
}
}
fout << shortestPathLength(graph) << '\n';
}
};
class Cartite {
std::ifstream fin;
std::ofstream fout;
int task;
int n, m;
int sx, sy;
std::vector <std::vector <bool>> blocked;
int di[4] = {-1, 0, 1, 0};
int dj[4] = {0, -1, 0, 1};
int edges;
std::vector <std::vector <int>> graph;
std::vector <bool> isEntry;
void blockCells(int i, int j, int p) {
blocked[i][j] = true;
if(p > 0) {
for(int k = 0; k < 4; k ++) {
int ii = i + di[k];
int jj = j + dj[k];
if(0 <= ii and ii < n and 0 <= jj and jj < m) {
blockCells(ii, jj, p-1);
}
}
}
}
void readInput() {
fin >> task;
fin >> n >> m;
blocked.resize(n, std::vector <bool> (m, false));
graph.resize(n * m);
isEntry.resize(n*m, false);
fin >> sx >> sy;
sx -= 1;
sy -= 1;
int foxes;
fin >> foxes;
for(int i = 0; i < foxes; i ++) {
int x, y, p;
fin >> x >> y >> p;
x -= 1;
y -= 1;
blockCells(x, y, p);
}
fin >> edges;
for(int e = 0; e < edges; e ++) {
int x1, y1, x2, y2;
fin >> x1 >> y1 >> x2 >> y2;
x1 -= 1;
y1 -= 1;
x2 -= 1;
y2 -= 1;
graph[x1*m+y1].push_back(x2*m+y2);
graph[x2*m+y2].push_back(x1*m+y1);
isEntry[x1*m+y1] = true;
isEntry[x2*m+y2] = true;
}
}
void solveTask1() {
std::vector <int> dist(n*m, 1e9);
std::queue <int> q;
q.push(sx*m+sy);
dist[sx*m+sy] = 0;
while(q.empty() == false) {
int node = q.front();
int i = node / m;
int j = node % m;
q.pop();
if(isEntry[node]) {
fout << i+1 << ' ' << j+1 << ' ' << dist[node] << '\n';
return;
}
for(int k = 0; k < 4; k ++) {
int ii = i + di[k];
int jj = j + dj[k];
if(0 <= ii and ii < n and 0 <= jj and jj < m and blocked[ii][jj] == false and dist[ii * m + jj] > dist[node] + 1) {
dist[ii * m + jj] = dist[node] + 1;
q.push(ii * m + jj);
}
}
}
}
void solveTask2() {
std::stack <int> ans;
std::stack <int> path;
std::unordered_map <int, bool> usedEdge;
for(int node = 0; node < n*m; node ++ ){
if(graph[node].empty() == false and blocked[node / m][node % m] == false) {
path.push(node);
break;
}
}
while(path.empty() == false) {
int node = path.top();
if(graph[node].empty() == false) {
int next = graph[node].back();
graph[node].pop_back();
if(usedEdge[node * n * m + next] == false) {
usedEdge[node * n * m + next] = true;
usedEdge[next * n * m + node] = true;
path.push(next);
}
} else {
path.pop();
ans.push(node);
}
}
while(ans.empty() == false){
fout << ans.top() / m + 1 << ' ' << ans.top() % m + 1 << '\n';
ans.pop();
}
}
public:
void solve() {
fin.open("cartite.in");
fout.open("cartite.out");
this->readInput();
if(task == 1) {
this->solveTask1();
}
else {
this->solveTask2();
}
fin.close();
fout.close();
}
};
//https://leetcode.com/problems/valid-arrangement-of-pairs/submissions/
class ValidArrangements {
std::ifstream fin;
std::ofstream fout;
public:
std::vector<std::vector<int>> validArrangement(std::vector<std::vector<int>>& pairs) {
std::unordered_map <int, int> norm;
std::unordered_map <int, int> revNorm;
int index = 0;
for(auto& p: pairs) {
for(int& x: p) {
if(norm[x] == 0) {
norm[x] = ++index;
revNorm[index] = x;
}
x = norm[x];
}
}
int n = index + 1;
std::vector <std::vector <int>> graph(n);
std::vector <int> in(n, 0), out(n, 0);
for(auto& p: pairs) {
graph[p[0]].push_back(p[1]);
out[p[0]] ++;
in[p[1]] ++;
}
std::stack <int> path;
std::vector <int> ans;
for(int i = 0; i < n; i ++) {
if(in[i] == out[i] - 1) {
path.push(i);
break;
}
}
if(path.empty() == true) {
path.push(1);
}
std::unordered_map <long long, bool> usedEdge;
while(path.empty() == false) {
int node = path.top();
if(graph[node].empty() == false) {
int next = graph[node].back();
graph[node].pop_back();
if(usedEdge[1LL * node * n + next] == false) {
usedEdge[1LL * node * n + next] = true;
path.push(next);
}
}
else {
path.pop();
ans.push_back(node);
}
}
std::vector <std::vector <int>> ret;
for(int i = 1; i < ans.size(); i ++) {
ret.push_back({revNorm[ans[i]], revNorm[ans[i-1]]});
}
std::reverse(ret.begin(), ret.end());
return ret;
}
void solve() {
fin.open("input.txt");
fout.open("output.txt");
int n;
fin >> n;
std::vector <std::vector <int>> v(n, std::vector<int>(2));
for(int i = 0; i < n; i ++) {
fin >> v[i][0] >> v[i][1];
}
auto ans = validArrangement(v);
for(auto x: ans) {
fout << x[0] << ' ' << x[1] << " - ";
}
fin.close();
fout.close();
}
};
class Dinic {
const int inf = 1e9;
struct FlowEdge {
int from, to;
int cap, flow = 0;
FlowEdge(int from, int to, int cap): from(from),to(to), cap(cap) {}
};
int n, m = 0;
int start, finish;
std::vector <FlowEdge> edges;
std::vector <std::vector <int>> adj;
std::vector <int> level, index;
bool bfs() {
std::fill(level.begin(), level.end(), -1);
level[start] = 0;
std::queue <int> q;
q.push(start);
while(q.empty() == false) {
int node = q.front();
q.pop();
for(int i: adj[node]) {
if(edges[i].cap <= edges[i].flow) {
continue;
}
if(level[edges[i].to] != -1) {
continue;
}
level[edges[i].to] = level[node] + 1;
q.push(edges[i].to);
}
}
return (level[finish] != -1);
}
int dfs(int node, int currentFlow) {
if(currentFlow <= 0) {
return 0;
}
if(node == finish) {
return currentFlow;
}
for(; index[node] < adj[node].size(); index[node] ++) {
int id = adj[node][index[node]];
int next = edges[id].to;
if(level[node] + 1 != level[next]) {
continue;
}
if(edges[id].cap <= edges[id].flow) {
continue;
}
int newFlow = dfs(next, std::min(currentFlow, edges[id].cap - edges[id].flow));
if(newFlow == 0) {
continue;
}
edges[id].flow += newFlow;
edges[id^1].flow -= newFlow;
return newFlow;
}
return 0;
}
public:
Dinic(int n, int start, int finish): n(n), start(start), finish(finish) {
adj.resize(n);
level.resize(n);
index.resize(n);
}
void addEdge(int from, int to, int cap) {
edges.emplace_back(from, to, cap);
edges.emplace_back(to, from, 0);
adj[from].push_back(m);
adj[to].push_back(m+1);
m += 2;
}
int flow() {
int f = 0;
while(true) {
if(bfs() == false) {
break;
}
std::fill(index.begin(), index.end(), 0);
while(int current = dfs(start, inf)) {
f += current;
}
}
return f;
}
std::vector <FlowEdge> getEdges() {
return this->edges;
}
};
class Harta {
std::ifstream fin;
std::ofstream fout;
int n;
std::vector <int> in, out;
void readInput() {
fin >> n;
in.resize(n);
out.resize(n);
for(int i = 0; i < n; i ++) {
fin >> in[i] >> out[i];
}
}
public:
void solve() {
fin.open("harta.in");
fout.open("harta.out");
this->readInput();
Dinic graph(2*n+2, 0, 2*n+1);
for(int i = 0; i < n; i ++) {
graph.addEdge(0, i+1, in[i]);
graph.addEdge(n+i+1, 2*n+1, out[i]);
for(int j = 0; j < n; j ++) {
if(i == j) {
continue;
}
graph.addEdge(i+1, n+j+1, 1);
}
}
fout << graph.flow() << '\n';
auto edges = graph.getEdges();
for(auto e: edges) {
if(e.flow > 0 and 1 <= e.from and e.from <= n and n+1 <= e.to and e.to <= 2*n)
fout << e.from << ' ' << e.to - n << '\n';
}
fin.close();
fout.close();
}
};
int main() {
Harta().solve();
return 0;
}