mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-19 22:23:51 +01:00
Merge pull request #3530 from Undeemiss/2-coloring-contract
CODINGCONTRACT: New "Proper 2-Coloring of a Graph" contract
This commit is contained in:
commit
b7e319f360
@ -1306,4 +1306,152 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return parseInt(ans, 10) === HammingDecode(data);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Proper 2-Coloring of a Graph",
|
||||
difficulty: 7,
|
||||
numTries: 5,
|
||||
desc: (data: [number, [number, number][]]): string => {
|
||||
return [
|
||||
`You are given the following tuple, representing a simple, undirected graph:\n`,
|
||||
`${JSON.stringify(data)}\n`,
|
||||
`The first element of the tuple represents the number of vertices in the graph.`,
|
||||
`Each vertex is a unique number in the range [0,${data[0]}].`,
|
||||
`The next element of the tuple represents the edge set of the graph.`,
|
||||
`Two vertices u,v in a graph are said to be adjacent if there exists an edge [u,v].`,
|
||||
`Note that the graph is undirected, meaning an edge [u,v] is the same as an edge [v,u].`,
|
||||
`You must construct a 2-coloring of the graph, meaning that you have to assign each`,
|
||||
`vertex in the graph a "color", either 0 or 1, such that no two adjacent vertices have`,
|
||||
`the same color. Submit your answer in the form of an array, where element i`,
|
||||
`represents the color of vertex i. If it is impossible to construct a 2-coloring of`,
|
||||
`the given graph, instead submit an empty array.\n\n`,
|
||||
`Examples:\n\n`,
|
||||
`Input: [4, [[1, 3], [1, 4], [2, 3], [2, 4]]]\n`,
|
||||
`Output: [0, 0, 1, 1]\n\n`,
|
||||
`Input: [3, [[1, 2], [1, 3], [2, 3]]]\n`,
|
||||
`Output: []`,
|
||||
].join(" ");
|
||||
},
|
||||
gen: (): [number, [number, number][]] => {
|
||||
//Generate two partite sets
|
||||
const n = Math.floor(Math.random() * 5) + 3;
|
||||
const m = Math.floor(Math.random() * 5) + 3;
|
||||
|
||||
//50% chance of spawning any given valid edge in the bipartite graph
|
||||
const edges: [number, number][] = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
for (let j = 0; j < m; j++) {
|
||||
if (Math.random() > 0.5) {
|
||||
edges.push([i, n + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Add an edge at random with no regard to partite sets
|
||||
let a = Math.floor(Math.random() * (n + m));
|
||||
let b = Math.floor(Math.random() * (n + m));
|
||||
if (a > b) [a, b] = [b, a]; //Enforce lower numbers come first
|
||||
if (a != b && !edges.includes([a, b])) {
|
||||
edges.push([a, b]);
|
||||
}
|
||||
|
||||
//Randomize array in-place using Durstenfeld shuffle algorithm.
|
||||
function shuffle(array: any[]): void {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
}
|
||||
|
||||
//Replace instances of the original vertex names in-place
|
||||
const vertexShuffler = Array.from(Array(n + m).keys());
|
||||
shuffle(vertexShuffler);
|
||||
for (let i = 0; i < edges.length; i++) {
|
||||
edges[i] = [vertexShuffler[edges[i][0]], vertexShuffler[edges[i][1]]];
|
||||
if (edges[i][0] > edges[i][1]) {
|
||||
//Enforce lower numbers come first
|
||||
[edges[i][0], edges[i][1]] = [edges[i][1], edges[i][0]];
|
||||
}
|
||||
}
|
||||
|
||||
//Shuffle the order of the edges themselves, as well
|
||||
shuffle(edges);
|
||||
|
||||
return [n + m, edges];
|
||||
},
|
||||
solver: (data: [number, [number, number][]], ans: string): boolean => {
|
||||
//Case where the player believes there is no solution
|
||||
if (ans == "[]") {
|
||||
//Helper function to get neighbourhood of a vertex
|
||||
function neighbourhood(vertex: number): number[] {
|
||||
const adjLeft = data[1].filter(([a, _]) => a == vertex).map(([_, b]) => b);
|
||||
const adjRight = data[1].filter(([_, b]) => b == vertex).map(([a, _]) => a);
|
||||
return adjLeft.concat(adjRight);
|
||||
}
|
||||
|
||||
//Verify that there is no solution by attempting to create a proper 2-coloring.
|
||||
const coloring: (number | undefined)[] = Array(data[0]).fill(undefined);
|
||||
while (coloring.some((val) => val === undefined)) {
|
||||
//Color a vertex in the graph
|
||||
const initialVertex: number = coloring.findIndex((val) => val === undefined);
|
||||
coloring[initialVertex] = 0;
|
||||
const frontier: number[] = [initialVertex];
|
||||
|
||||
//Propogate the coloring throughout the component containing v greedily
|
||||
while (frontier.length > 0) {
|
||||
const v: number = frontier.pop() || 0;
|
||||
const neighbors: number[] = neighbourhood(v);
|
||||
|
||||
//For each vertex u adjacent to v
|
||||
for (const id in neighbors) {
|
||||
const u: number = neighbors[id];
|
||||
|
||||
//Set the color of u to the opposite of v's color if it is new,
|
||||
//then add u to the frontier to continue the algorithm.
|
||||
if (coloring[u] === undefined) {
|
||||
if (coloring[v] === 0) coloring[u] = 1;
|
||||
else coloring[u] = 0;
|
||||
|
||||
frontier.push(u);
|
||||
}
|
||||
|
||||
//Assert u,v do not have the same color
|
||||
else if (coloring[u] === coloring[v]) {
|
||||
//If u,v do have the same color, no proper 2-coloring exists, meaning
|
||||
//the player was correct to say there is no proper 2-coloring of the graph.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If this code is reached, there exists a proper 2-coloring of the input
|
||||
//graph, and thus the player was incorrect in submitting no answer.
|
||||
return false;
|
||||
}
|
||||
|
||||
//Sanitize player input
|
||||
const sanitizedPlayerAns: string = removeBracketsFromArrayString(ans);
|
||||
const sanitizedPlayerAnsArr: string[] = sanitizedPlayerAns.split(",");
|
||||
const coloring: number[] = sanitizedPlayerAnsArr.map((val) => parseInt(val));
|
||||
|
||||
//Solution provided case
|
||||
if (coloring.length == data[0]) {
|
||||
const edges = data[1];
|
||||
const validColors = [0, 1];
|
||||
//Check that the provided solution is a proper 2-coloring
|
||||
return edges.every(([a, b]) => {
|
||||
const aColor = coloring[a];
|
||||
const bColor = coloring[b];
|
||||
return (
|
||||
validColors.includes(aColor) && //Enforce the first endpoint is color 0 or 1
|
||||
validColors.includes(bColor) && //Enforce the second endpoint is color 0 or 1
|
||||
aColor != bColor //Enforce the endpoints are different colors
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
//Return false if the coloring is the wrong size
|
||||
else return false;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user