第一行输入一个正整数
,代表二分图的节点数量。
第二行输入
个正整数
,代表每个节点的度数。
如果答案不存在,直接输出
。
否则,请参考下方的格式输出。
第一行输出一个整数
代表边的数量。
接下来的
行,每行输出两个正整数
代表节点
和节点
有一条边连接。
构造的图可以包含重边,但不能包含自环。构造的最终的图可以不连通,你只需要保证每个连通分量均为二分图。
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
3 1 2 1
2 1 2 2 3
构造的图是一棵树,显然所有树都是二分图。
3 2 2 2
-1
只能构造一个三元环,显然不是二分图。
import java.util.*;
// @@@ 二分图:节点划分两部分,两部分的度相等 → 01背包 装满sum/2
// 要知道具体的分配方案,由dp[n][v]反推
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] d = new int[n];
int sum = 0;
for (int i = 0; i < n; i++) {
d[i] = in.nextInt();
sum += d[i];
}
if (sum % 2 != 0) { // 度数和应该为偶数 → -1
System.out.println(-1);
return;
}
int v = sum / 2;
int[][] dp = new int[n + 1][v + 1]; // 01背包
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= v; j++) {
if (j >= d[i - 1]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - d[i - 1]] + d[i - 1]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
if (dp[n][v] != v) { // 没装满 → -1
System.out.println(-1);
return;
}
Set<Integer> set = new HashSet<>();
for (int i = n, j = v; i > 0 && j > 0; ) { // 反推所选方案
if (dp[i][j] == dp[i - 1][j]) { // 不选节点i
i--;
} else { // 选节点i
set.add(i - 1);
j -= d[i - 1];
i--;
}
}
List<Integer> list1 = new ArrayList<>(set);
List<Integer> list2 = new ArrayList<>();
for (int i = 0; i < n; i++) if (!set.contains(i)) list2.add(i);
// System.out.println(list1.size() + " " + list2.size() + " " + v);
StringBuilder sb = new StringBuilder();
sb.append(v).append('\n');
for (int i = 0, j = 0; i < list1.size() && j < list2.size();) {
while (d[list1.get(i)] > 0 && d[list2.get(j)] > 0) {
sb.append(list1.get(i) + 1).append(' ').append(list2.get(j) + 1).append('\n');
d[list1.get(i)]--;
d[list2.get(j)]--;
}
if (d[list1.get(i)] == 0) i++;
if (d[list2.get(j)] == 0) j++;
}
System.out.println(sb.toString());
}
}