mirror of
https://github.com/InternLM/InternBootcamp.git
synced 2026-04-19 12:58:04 +00:00
322 lines
10 KiB
Python
Executable file
322 lines
10 KiB
Python
Executable file
"""#
|
|
|
|
### 谜题描述
|
|
Valera had an undirected connected graph without self-loops and multiple edges consisting of n vertices. The graph had an interesting property: there were at most k edges adjacent to each of its vertices. For convenience, we will assume that the graph vertices were indexed by integers from 1 to n.
|
|
|
|
One day Valera counted the shortest distances from one of the graph vertices to all other ones and wrote them out in array d. Thus, element d[i] of the array shows the shortest distance from the vertex Valera chose to vertex number i.
|
|
|
|
Then something irreparable terrible happened. Valera lost the initial graph. However, he still has the array d. Help him restore the lost graph.
|
|
|
|
Input
|
|
|
|
The first line contains two space-separated integers n and k (1 ≤ k < n ≤ 105). Number n shows the number of vertices in the original graph. Number k shows that at most k edges were adjacent to each vertex in the original graph.
|
|
|
|
The second line contains space-separated integers d[1], d[2], ..., d[n] (0 ≤ d[i] < n). Number d[i] shows the shortest distance from the vertex Valera chose to the vertex number i.
|
|
|
|
Output
|
|
|
|
If Valera made a mistake in his notes and the required graph doesn't exist, print in the first line number -1. Otherwise, in the first line print integer m (0 ≤ m ≤ 106) — the number of edges in the found graph.
|
|
|
|
In each of the next m lines print two space-separated integers ai and bi (1 ≤ ai, bi ≤ n; ai ≠ bi), denoting the edge that connects vertices with numbers ai and bi. The graph shouldn't contain self-loops and multiple edges. If there are multiple possible answers, print any of them.
|
|
|
|
Examples
|
|
|
|
Input
|
|
|
|
3 2
|
|
0 1 1
|
|
|
|
|
|
Output
|
|
|
|
3
|
|
1 2
|
|
1 3
|
|
3 2
|
|
|
|
|
|
Input
|
|
|
|
4 2
|
|
2 0 1 3
|
|
|
|
|
|
Output
|
|
|
|
3
|
|
1 3
|
|
1 4
|
|
2 3
|
|
|
|
|
|
Input
|
|
|
|
3 1
|
|
0 0 0
|
|
|
|
|
|
Output
|
|
|
|
-1
|
|
|
|
Here is a reference code to solve this task. You can use this to help you genereate cases or validate the solution.
|
|
```python
|
|
n, k = map(int, raw_input().split())
|
|
v = [int(i) for i in raw_input().split()]
|
|
|
|
s = [0] * n
|
|
for x in v:
|
|
s[x] += 1
|
|
|
|
idx = [[] for i in range(n)]
|
|
for i, val in enumerate(v):
|
|
idx[val].append(i + 1)
|
|
|
|
for i in range(n - 1):
|
|
if s[0] != 1 or s[i] * (k - (i != 0)) < s[i + 1]:
|
|
print -1
|
|
break
|
|
else:
|
|
print n - 1
|
|
ans = []
|
|
for i in range(1, n):
|
|
z = 0
|
|
c = 0
|
|
for x in idx[i]:
|
|
ans.append(str(idx[i - 1][z]) + ' ' + str(x))
|
|
c += 1
|
|
if c == k - (i != 1):
|
|
c = 0
|
|
z += 1
|
|
print('\n'.join(ans))
|
|
```
|
|
|
|
|
|
请完成上述谜题的训练场环境类实现,包括所有必要的方法。
|
|
"""
|
|
|
|
from bootcamp import Basebootcamp
|
|
import random
|
|
import re
|
|
from collections import defaultdict
|
|
from math import ceil
|
|
|
|
class Crestoregraphbootcamp(Basebootcamp):
|
|
def __init__(self, max_n=20, **params):
|
|
self.max_n = max_n
|
|
super().__init__(**params)
|
|
|
|
def case_generator(self):
|
|
# Generate valid or invalid cases
|
|
generate_valid = random.random() < 0.7 # 70% valid, 30% invalid
|
|
n = random.randint(2, self.max_n)
|
|
valid = True
|
|
|
|
# Generate initial valid layers (s)
|
|
s = [1]
|
|
remaining = n - 1
|
|
while remaining > 0:
|
|
next_s = random.randint(1, remaining)
|
|
s.append(next_s)
|
|
remaining -= next_s
|
|
|
|
# Calculate minimal required k
|
|
max_k = 0
|
|
for i in range(len(s)-1):
|
|
current_s = s[i]
|
|
next_s = s[i+1]
|
|
required_k = ceil(next_s / current_s) + (i != 0)
|
|
max_k = max(max_k, required_k)
|
|
k = max_k
|
|
|
|
# Generate d array
|
|
d = []
|
|
for i, count in enumerate(s):
|
|
d.extend([i] * count)
|
|
random.shuffle(d)
|
|
|
|
# Introduce invalid conditions if needed
|
|
if not generate_valid:
|
|
invalid_type = random.choice([0, 1, 2, 3])
|
|
if invalid_type == 0:
|
|
# Break s[0] to invalid count
|
|
s[0] = 0
|
|
d = []
|
|
for i, cnt in enumerate(s):
|
|
d.extend([i] * cnt)
|
|
d[random.randint(0, len(d)-1)] = 0 # Ensure at least one 0
|
|
elif invalid_type == 1 and max_k > 1:
|
|
# Reduce k to make insufficient
|
|
k = random.randint(1, max_k - 1)
|
|
elif invalid_type == 2 and len(s) > 1:
|
|
# Disrupt d array hierarchy
|
|
max_d = len(s) - 1
|
|
if max_d + 1 < n:
|
|
idx = random.randint(0, len(d)-1)
|
|
d[idx] = max_d + 1
|
|
elif invalid_type == 3 and len(s) > 2:
|
|
# Make a layer exceed parent capacity
|
|
i = random.randint(1, len(s)-2)
|
|
s[i+1] = s[i] * (k - 1) + 1
|
|
d = []
|
|
for layer, cnt in enumerate(s):
|
|
d.extend([layer] * cnt)
|
|
random.shuffle(d)
|
|
|
|
return {'n': n, 'k': k, 'd': d}
|
|
|
|
@staticmethod
|
|
def prompt_func(question_case):
|
|
n = question_case['n']
|
|
k = question_case['k']
|
|
d = question_case['d']
|
|
input_lines = [f"{n} {k}", ' '.join(map(str, d))]
|
|
input_str = '\n'.join(input_lines)
|
|
prompt = f"""Valera had an undirected connected graph without self-loops or multiple edges, where each vertex has at most {k} edges. He recorded the shortest distances from one vertex to all others in array d. Your task is to determine if the graph can be restored. If not, output -1. Otherwise, output the number of edges followed by the edges. Format your answer within [answer] and [/answer].
|
|
Input:
|
|
{input_str}"""
|
|
return prompt
|
|
|
|
@staticmethod
|
|
def extract_output(output):
|
|
matches = re.findall(r'\[answer\](.*?)\[/answer\]', output, re.DOTALL)
|
|
if not matches:
|
|
return None
|
|
answer = matches[-1].strip()
|
|
lines = [line.strip() for line in answer.split('\n') if line.strip()]
|
|
if not lines:
|
|
return None
|
|
if lines[0] == '-1':
|
|
return '-1'
|
|
try:
|
|
m = int(lines[0])
|
|
edges = []
|
|
for line in lines[1:m+1]:
|
|
parts = line.split()
|
|
if len(parts) != 2:
|
|
continue
|
|
a, b = map(int, parts)
|
|
if a == b:
|
|
continue
|
|
a, b = sorted((a, b))
|
|
edges.append((a, b))
|
|
edges = list(set(edges))
|
|
edges.sort()
|
|
return {'m': len(edges), 'edges': edges}
|
|
except:
|
|
return None
|
|
|
|
@classmethod
|
|
def _verify_correction(cls, solution, identity):
|
|
def reference_solve(n, k, d_list):
|
|
v = d_list
|
|
s = [0] * n
|
|
for x in v:
|
|
if x >= n or x < 0:
|
|
return (-1, [])
|
|
s[x] += 1
|
|
|
|
if s[0] != 1:
|
|
return (-1, [])
|
|
|
|
max_d = max(v)
|
|
for i in range(max_d + 1):
|
|
if s[i] == 0:
|
|
return (-1, [])
|
|
|
|
for i in range(max_d):
|
|
allowed = s[i] * (k - (i != 0))
|
|
if s[i+1] > allowed:
|
|
return (-1, [])
|
|
|
|
idx = [[] for _ in range(n)]
|
|
for node, dist in enumerate(v, 1):
|
|
idx[dist].append(node)
|
|
|
|
edges = []
|
|
try:
|
|
for dist in range(1, max_d + 1):
|
|
parents = idx[dist-1]
|
|
children = idx[dist]
|
|
if not parents or not children:
|
|
return (-1, [])
|
|
|
|
slots_per_parent = k - (1 if dist-1 !=0 else 0)
|
|
required_parents = ceil(len(children) / slots_per_parent)
|
|
if len(parents) < required_parents:
|
|
return (-1, [])
|
|
|
|
for i, child in enumerate(children):
|
|
parent_idx = i // slots_per_parent
|
|
if parent_idx >= len(parents):
|
|
return (-1, [])
|
|
edges.append((parents[parent_idx], child))
|
|
except:
|
|
return (-1, [])
|
|
|
|
unique_edges = set()
|
|
degrees = defaultdict(int)
|
|
for a, b in edges:
|
|
if a == b:
|
|
continue
|
|
a, b = sorted((a, b))
|
|
unique_edges.add((a, b))
|
|
degrees[a] += 1
|
|
degrees[b] += 1
|
|
if degrees[a] > k or degrees[b] > k:
|
|
return (-1, [])
|
|
|
|
return (len(unique_edges), sorted(unique_edges))
|
|
|
|
n = identity['n']
|
|
k = identity['k']
|
|
d_list = identity['d']
|
|
ref_m, ref_edges = reference_solve(n, k, d_list)
|
|
|
|
if solution == '-1':
|
|
return ref_m == -1
|
|
|
|
if isinstance(solution, dict):
|
|
user_m = solution.get('m', 0)
|
|
user_edges = set(tuple(e) for e in solution.get('edges', []))
|
|
if ref_m == -1:
|
|
return False
|
|
|
|
# Check edge count and content
|
|
if user_m != ref_m or user_edges != set(ref_edges):
|
|
return False
|
|
|
|
# Check degree constraints
|
|
degrees = defaultdict(int)
|
|
for a, b in user_edges:
|
|
degrees[a] += 1
|
|
degrees[b] += 1
|
|
if degrees[a] > k or degrees[b] > k:
|
|
return False
|
|
|
|
# Check connectivity and distances
|
|
try:
|
|
adj = defaultdict(list)
|
|
for a, b in user_edges:
|
|
adj[a].append(b)
|
|
adj[b].append(a)
|
|
|
|
source = d_list.index(0) + 1
|
|
visited = {source: 0}
|
|
queue = [source]
|
|
for node in queue:
|
|
for neighbor in adj[node]:
|
|
if neighbor not in visited:
|
|
visited[neighbor] = visited[node] + 1
|
|
queue.append(neighbor)
|
|
|
|
for i, d_val in enumerate(d_list):
|
|
node = i + 1
|
|
if visited.get(node, -1) != d_val:
|
|
return False
|
|
except:
|
|
return False
|
|
|
|
return True
|
|
|
|
return False
|