第一行输入一个正整数
,代表二分图的节点数量。
第二行输入
个正整数
,代表每个节点的度数。
如果答案不存在,直接输出
。
否则,请参考下方的格式输出。
第一行输出一个整数
代表边的数量。
接下来的
行,每行输出两个正整数
代表节点
和节点
有一条边连接。
构造的图可以包含重边,但不能包含自环。构造的最终的图可以不连通,你只需要保证每个连通分量均为二分图。
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
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());
}
} 只能想到暴力解法了 。。。。 n = int(input()) d = list(map(int, input().split())) nodes = [] for idx, node in enumerate(d): nodes.append((idx, node)) # (node名称, 度数) total_sides = sum(d) if total_sides % 2 != 0: print(-1) exit(0) target_sides = int(total_sides // 2) # 二分图的性质 # 1. 左右两边度相等 # 2. 左边度的最大值不能超过右边节点数量,右边同理。因为允许重边,所以不需要进一步考虑节点和边的对应关系 sorted_nodes = sorted(nodes, key=lambda x: x[1], reverse=True) # 先想办法让左右两边度相等 curr_side = 0 def dfs(curr_side, target_side, begin, n): if curr_side == target_side: return [] if begin == n-1: return None for i in range(begin+1, n): curr_node = sorted_nodes[i] choices = dfs(curr_side+curr_node[1], target_side, i, n) # 选择当前node if choices is not None: choices.append(list(curr_node)) return choices choices = dfs(curr_side, target_side, i, n) # 不选择当前node if choices is not None: return choices return None left_list = dfs(0, target_sides, -1, n) if left_list is None: print(-1) exit(0) max_sides = left_list[0][1] if max_sides + len(left_list) > n: print(-1) exit(0) left_list = sorted(left_list, key=lambda x: x[0]) # 按序号排序 right_list = [] last_idx = 0 for each in left_list: for i in range(last_idx, each[0]): right_list.append(list(nodes[i])) last_idx = each[0] + 1 for i in range(last_idx, n): right_list.append(list(nodes[i])) # 打印边数量 print(target_sides) # 从left_list一个一个取值 然后对应到right_list curr_right_idx = 0 for each in left_list: while each[1] > 0: tmp_r = right_list[curr_right_idx] if tmp_r[1] == 0: curr_right_idx += 1 continue tmp_min = min(tmp_r[1], each[1]) tmp_r[1] -= tmp_min each[1] -= tmp_min for _ in range(tmp_min): print(each[0]+1, tmp_r[0]+1) # 序号要+1