Indoorienteering: Difference between revisions

From programming_contest
Jump to navigation Jump to search
imported>Kmk21
Created page with "= Introduction = This problem whether a graph contains a hamiltonian cycle of a given length = Solutions= == Greedy/BFS == === Idea === So long as each internal node uses..."
 
imported>Kmk21
No edit summary
 
(5 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Introduction =
= Introduction =
This problem whether a graph contains a [[hamiltonian cycle]] of a given length
This problem whether a graph contains a [[hamiltonian cycle]] of a given length.


= Solutions=
= Solutions=
== Greedy/BFS ==
== Brute Force ==
=== Idea ===
=== Idea ===
So long as each internal node uses both of its values with some adjacent node, it doesn't matter which edges are used by which values. This is only true because it's a tree. If there were loops, it would get much more complicated.
Hamiltonian Cycle is NP complete ....must do brute force. Iterate over all possible permutations of points and check the length.


Two cases:
Further, it's clear it's brute-force-ish because n=14.
# No internal nodes: just assign the same to values to the nodes
# Internal nodes: pick a "start" internal node. Assign it two values (0 and 1 sound good). Alternating between the two values, choose an adjacent node, and fix it's first value to the chosen value from our start node. This guarantees that both values are used by some edge. Run BFS, and when you reach a node, choose the next unused value as the nodes second value (remember we assigned it's first value from the preceding node), and the assign the first values of all it's successor nodes to one of the two values (ensuring both are used somewhere). When you reach an edge node, assign any frequency.


===Runtime===
===Runtime===
* n
* n! = 87178291200


=== Code ===
== Fixed Starting Point ==
=== Idea ===
It's a cycle, so therefore we'll reach every point. If we fix a start point, we eliminate a dimension.
===Runtime===
* (n-1)! = 6227020800
 
== Divide and Conquer ==
=== Idea ===
Split the points up into two equal halves. Compute the lengths of each side to see if any of the possibilities add up. Fix the midpoint so the calculation of each side has a fixed start and end point.
===Runtime===
* (n-1 choose n/2) * n/2! * n/2!
* (n-1)!/(n/2!)^2 * (n/2!)^2
* (n-1)! :(


== Divide and Conquer with Hashmap ==
=== Idea ===
When you compute the first side, store all the lengths into the hashset. When you compute the second half, simply check whether the hashmap contains total length minus length second half length. Here we see why it's important to fix the midpoint. Without a fixed midpoint, we can't calculate the two halves independent of each other, as the distance of side 1 would depend on the first point on side 2 (and vice versa).
===Runtime===
* (n-1 choose n/2) * (n/2! + n/2!)
* n-1!/(n/2!)^2 * 2 * n/2!
* 2 * (n-1)! / (n/2!) = 17,297,280
===Code===
==== Solution - Java ====
==== Solution - Java ====
<syntaxhighlight line lang="java">
<syntaxhighlight line lang="java">
import java.util.*;
import java.util.*;
public class h {
public class i {
Scanner in=new Scanner(System.in);
Scanner in=new Scanner(System.in);
public static void main(String[] args) {
public static void main(String[] args) {
new h().go();
new i().go();
}
}
private void go() {
private void go() {
int n=in.nextInt();
int n=in.nextInt();
int ord=0;
long l=in.nextLong();
HashMap<Integer, HashSet<Integer>>hm=new HashMap<Integer,HashSet<Integer>>();
long[][] d=new long[n][n];
for(int i=1;i<n;i++){
for(int i=0;i<n;i++)for(int j=0;j<n;j++)d[i][j]=in.nextLong();
int ii=in.nextInt();
if(n==2){
int j=in.nextInt();
System.out.printf("%s\n", d[0][1]+d[1][0]==l?"possible":"impossible");
if(!hm.containsKey(ii))hm.put(ii, new HashSet<Integer>());
System.exit(0);
if(!hm.containsKey(j))hm.put(j, new HashSet<Integer>());
hm.get(ii).add(j);
hm.get(j).add(ii);
}
}
TreeSet<Integer> visited=new TreeSet<Integer>();
for(int i=1;i<n;i++){//it's a cycle...assume it starts at 0 with midpoint i
int[][] ass=new int[n+1][2];
for(int j=0;j<1<<(n-2);j++)if(Integer.bitCount(j)==(n-2)/2&&j<(j^((1<<(n-2))-1))){
for(int i=0;i<ass.length;i++)for(int j=0;j<ass[0].length;j++)ass[i][j]=-1;
int[] inn=new int[(n-2)/2],out=new int[n-2-(n-2)/2];
Queue<Integer> q=new LinkedList<Integer>();
int inni=0,outi=0;
for(int i:hm.keySet())if(hm.get(i).size()!=1){//Start with a non-leaf node
for(int k=1;k<n;k++)if(k!=i){//skip the breakpoint
q.add(i);
if(((j>>(k-(k>i?2:1)))&1)==1)inn[inni++]=k;
ass[i][0]=ord++;//assumption is this comes from previous node
else out[outi++]=k;
break;
}
HashSet<Long> l1=solve(inn,d,0,i);
HashSet<Long> l2=solve(out,d,i,0);
for(long ls:l1)if(l2.contains(l-ls)){
System.out.println("possible");
System.exit(0);
}
}
}
}
if(q.isEmpty()){//degenerate case
System.out.println("impossible");
System.out.println("0 1");
}
System.out.println("1 0");
private HashSet<Long> solve(int[] inn, long[][] d,int f,int l) {
System.exit(0);
HashSet<Long> r=new HashSet<Long>();
if(inn.length==0){
r.add(d[f][l]);
return r;
}
}
while(!q.isEmpty()){
do{
int on=q.poll();
long ans=0;
int s=1;
ans+=d[f][inn[0]];
ass[on][1]=ord++;
ans+=d[inn[inn.length-1]][l];
visited.add(on);
for(int i=0;i<inn.length-1;i++)ans+=d[inn[i]][inn[i+1]];
for(int i:hm.get(on)){
r.add(ans);
if(!visited.contains(i)){
} while(nextPermutation(inn));
ass[i][0]=ass[on][s++%2];//for most nodes we can just use the new frequency...but for first node, gotta ensure both get used..so just alternate
return r;
q.offer(i);
}
}
public boolean nextPermutation(int[] in){
for(int i=in.length-2;i>=0;i--)if(in[i]<in[i+1]){
for(int j=in.length-1;j>i;j--)if(in[j]>in[i]){
int t=in[i];
in[i]=in[j];
in[j]=t;
break;
}
for(int j=i+1;j<in.length-(j-i);j++){
int t=in[j];
in[j]=in[in.length-(j-i)];
in[in.length-(j-i)]=t;
}
}
return true;
}
}
for(int i=1;i<n+1;i++)System.out.printf("%d %d\n",ass[i][0],ass[i][1]);
return false;
}
}
}
}
</syntaxhighlight>
</syntaxhighlight>
[[Category:nwerc2014]]
[[Category:nwerc2014]]
[[Category:ICPC Problems]]
[[Category:ICPC Problems]]
[[Category:BFS]]
[[Category:Graph]]
[[Category:Greedy]]
[[Category:NP]]
[[Category:Hashmap]]
[[Category:Combinatorics]]
[[Category:divide and Conquer]]
[[Category:Algorithm Medium]]
[[Category:Algorithm Medium]]
[[Category:Implementation Medium]]
[[Category:Implementation Medium]]

Latest revision as of 04:34, 9 February 2015

Introduction

This problem whether a graph contains a hamiltonian cycle of a given length.

Solutions

Brute Force

Idea

Hamiltonian Cycle is NP complete ....must do brute force. Iterate over all possible permutations of points and check the length.

Further, it's clear it's brute-force-ish because n=14.

Runtime

  • n! = 87178291200

Fixed Starting Point

Idea

It's a cycle, so therefore we'll reach every point. If we fix a start point, we eliminate a dimension.

Runtime

  • (n-1)! = 6227020800

Divide and Conquer

Idea

Split the points up into two equal halves. Compute the lengths of each side to see if any of the possibilities add up. Fix the midpoint so the calculation of each side has a fixed start and end point.

Runtime

  • (n-1 choose n/2) * n/2! * n/2!
  • (n-1)!/(n/2!)^2 * (n/2!)^2
  • (n-1)! :(

Divide and Conquer with Hashmap

Idea

When you compute the first side, store all the lengths into the hashset. When you compute the second half, simply check whether the hashmap contains total length minus length second half length. Here we see why it's important to fix the midpoint. Without a fixed midpoint, we can't calculate the two halves independent of each other, as the distance of side 1 would depend on the first point on side 2 (and vice versa).

Runtime

  • (n-1 choose n/2) * (n/2! + n/2!)
  • n-1!/(n/2!)^2 * 2 * n/2!
  • 2 * (n-1)! / (n/2!) = 17,297,280

Code

Solution - Java

import java.util.*;
public class i {
	Scanner in=new Scanner(System.in);
	public static void main(String[] args) {
		new i().go();
	}
	private void go() {
		int n=in.nextInt();
		long l=in.nextLong();
		long[][] d=new long[n][n];
		for(int i=0;i<n;i++)for(int j=0;j<n;j++)d[i][j]=in.nextLong();
		if(n==2){
			System.out.printf("%s\n", d[0][1]+d[1][0]==l?"possible":"impossible");
			System.exit(0);
		}
		for(int i=1;i<n;i++){//it's a cycle...assume it starts at 0 with midpoint i
			for(int j=0;j<1<<(n-2);j++)if(Integer.bitCount(j)==(n-2)/2&&j<(j^((1<<(n-2))-1))){
				int[] inn=new int[(n-2)/2],out=new int[n-2-(n-2)/2];
				int inni=0,outi=0;
				for(int k=1;k<n;k++)if(k!=i){//skip the breakpoint
					if(((j>>(k-(k>i?2:1)))&1)==1)inn[inni++]=k;
					else out[outi++]=k;
				}
				HashSet<Long> l1=solve(inn,d,0,i);
				HashSet<Long> l2=solve(out,d,i,0);
				for(long ls:l1)if(l2.contains(l-ls)){
					System.out.println("possible");
					System.exit(0);
				}
			}
		}
		System.out.println("impossible");
	}
	private HashSet<Long> solve(int[] inn, long[][] d,int f,int l) {
		HashSet<Long> r=new HashSet<Long>();
		if(inn.length==0){
			r.add(d[f][l]);
			return r;
		}
		do{
			long ans=0;
			ans+=d[f][inn[0]];
			ans+=d[inn[inn.length-1]][l];
			for(int i=0;i<inn.length-1;i++)ans+=d[inn[i]][inn[i+1]];
			r.add(ans);
		} while(nextPermutation(inn));
		return r;
	}
	public boolean nextPermutation(int[] in){
		for(int i=in.length-2;i>=0;i--)if(in[i]<in[i+1]){
			for(int j=in.length-1;j>i;j--)if(in[j]>in[i]){
				int t=in[i];
				in[i]=in[j];
				in[j]=t;
				break;
			}
			for(int j=i+1;j<in.length-(j-i);j++){
				int t=in[j];
				in[j]=in[in.length-(j-i)];
				in[in.length-(j-i)]=t;
			}
			return true;
		}
		return false;
	}
}