[4_1_fence6] Finding Shortest Cycle

Fence Loops

The fences that surround Farmer Brown's collection of pastures have gotten out of control. They are made up of straight segments from 1 through 200 feet long that join together only at their endpoints though sometimes more than two fences join together at a given endpoint. The result is a web of fences enclosing his pastures. Farmer Brown wants to start to straighten things out. In particular, he wants to know which of the pastures has the smallest perimeter.

Farmer Brown has numbered his fence segments from 1 to N (N = the total number of segments). He knows the following about each fence segment:

  • the length of the segment
  • the segments which connect to it at one end
  • the segments which connect to it at the other end.
Happily, no fence connects to itself.

Given a list of fence segments that represents a set of surrounded pastures, write a program to compute the smallest perimeter of any pasture. As an example, consider a pasture arrangement, with fences numbered 1 to 10 that looks like this one (the numbers are fence ID numbers):

 
           1
   +---------------+
   |\             /|
  2| \7          / |
   |  \         /  |
   +---+       /   |6
   | 8  \     /10  |
  3|     \9  /     |
   |      \ /      |
   +-------+-------+
       4       5

The pasture with the smallest perimeter is the one that is enclosed by fence segments 2, 7, and 8.

PROGRAM NAME: fence6

INPUT FORMAT

Line 1:N (1 <= N <= 100)
Line 2..3*N+1:

N sets of three line records:

  • The first line of each record contains four integers: s, the segment number (1 <= s <= N); Ls, the length of the segment (1 <= Ls <= 255); N1s (1 <= N1s <= 8) the number of items on the subsequent line; and N2sthe number of items on the line after that (1 <= N2s <= 8).
  • The second line of the record contains N1 integers, each representing a connected line segment on one end of the fence.
  • The third line of the record contains N2 integers, each representing a connected line segment on the other end of the fence.

SAMPLE INPUT (file fence6.in)

 
10
1 16 2 2
2 7
10 6
2 3 2 2
1 7
8 3
3 3 2 1
8 2
4
4 8 1 3
3
9 10 5
5 8 3 1
9 10 4
6
6 6 1 2 
5 
1 10
7 5 2 2 
1 2
8 9
8 4 2 2
2 3
7 9
9 5 2 3
7 8
4 5 10
10 10 2 3
1 6
4 9 5

OUTPUT FORMAT

The output file should contain a single line with a single integer that represents the shortest surrounded perimeter.

SAMPLE OUTPUT (file fence6.out)

 
12












Finding shortest (minimum) cycle in a graph: (directed/undirected?)
(1) enumerate every edge, remove it and find the shortest path between the endpoints of this edge.
(2) DFS
(3) Floyd-Warshall algorithm
for (int i = 0; i < node_num; ++i) {
        dist[i].resize(node_num);
        for (int j = 0; j < node_num; ++j)
                dist[i][j] = adj_matrix[i][j];
}
int ans = INT_MAX;
for (int k = 0; k < node_num; ++k) {
        for (int i = 0; i < k; ++i)
                for (int j = i+1; j < k; ++j)
                        ans = min(ans, dist[i][j] + adj_matrix[i][k] + adj_matrix[k][j]);
        for (int i = 0; i < node_num; ++i)
                for (int j = 0; j < node_num; ++j)
                        dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}


My code is using the floyd algorithm:
#include <cstdio>
#include <climits>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <functional>

using namespace std;

int main() {
	freopen("fence6.in", "r", stdin);
	freopen("fence6.out", "w", stdout);
	int n;
	scanf("%d", &n);
	map<set<int>, int> node_hash;
	vector<map<int, int> > adj_list;
	vector<vector<int> > adj_matrix;
	for (int i = 0; i < n; ++i) {
		int s, len, num_left, num_right;
		scanf("%d%d%d%d", &s, &len, &num_left, &num_right);
		set<int> segments;
		segments.insert(s);
		while (num_left--) {
			int a;
			scanf("%d", &a);
			segments.insert(a);
		}
		if (node_hash.find(segments) == node_hash.end()) {
			node_hash[segments] = adj_list.size();
			adj_list.push_back(map<int, int>());
		}
		int node_left = node_hash[segments];
		segments.clear();
		segments.insert(s);
		while (num_right--) {
			int a;
			scanf("%d", &a);
			segments.insert(a);
		}
		if (node_hash.find(segments) == node_hash.end()) {
			node_hash[segments] = adj_list.size();
			adj_list.push_back(map<int, int>());
		}
		int node_right = node_hash[segments];
		adj_list[node_left][node_right] = len;
		adj_list[node_right][node_left] = len;
	}
	int node_num = adj_list.size();
	adj_matrix.resize(node_num);
	const int MAX_LEN = 255*100;
	for (int i = 0; i < node_num; ++i) {
		adj_matrix[i].resize(node_num);
		for (int j = 0; j < node_num; ++j) {
			if (adj_list[i].find(j) == adj_list[i].end())
				adj_matrix[i][j] = MAX_LEN;
			else
				adj_matrix[i][j] = adj_list[i][j];
		}
	}
	vector<vector<int> > dist;
	dist.resize(node_num);
	for (int i = 0; i < node_num; ++i) {
		dist[i].resize(node_num);
		for (int j = 0; j < node_num; ++j)
			dist[i][j] = adj_matrix[i][j];
	}
	int ans = INT_MAX;
	for (int k = 0; k < node_num; ++k) {
		for (int i = 0; i < k; ++i)
			for (int j = i+1; j < k; ++j)
				ans = min(ans, dist[i][j] + adj_matrix[i][k] + adj_matrix[k][j]);
		for (int i = 0; i < node_num; ++i)
			for (int j = 0; j < node_num; ++j)
				dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
	}
	printf("%d\n", ans);
	return 0;
}

Fence Loops
Hal Burch

The first problem is to switch the problem into a graph. The easiest graph representation to use is an adjacency list, where each node has the list of edges which are incident to it. The problem specifies that the endpoint of each edge will coincide with no more than 8 other endpoints.

For simplicity, let the endpoints of fence segments be the nodes of the graph, and the edges be of two types: the ones across fence segments and the ones connecting points which are at the same location (another alternative is to just have one node for each location).

Given the input, it's fairly simple to determine which side of fence A that fence B is connected to by determining which adjaency list of fence B contains A.

Once the graph representation is done, the problem is to find the shortest cycle (edges between nodes at the same location are considered to have length zero). For each edge, find the minimum length simple cycle which contains it by deleting that edge and finding the minimum length path from one end of the edge to the other. We can just use Dijkstra's to determine the shortest path.

 
 
#include <stdio.h>

/* maximum number of segments */
#define MAXS 100

/* an edge is a fence segment */
/* a place is a side of a fence segment */
/* there are two places for each edge */

/* conn is the index of the incident edge */
/* alist is the index of the incident place */

int conn[2*MAXS][8]; /* one edge list for each side of each segment */
int ccnt[2*MAXS]; /* number of incident edges */
int length[MAXS]; /* length of the segments */

int alist[2*MAXS][8]; /* adjacency list for each end of each segment */

int nfence; /* numbor of fences */
int npl; /* number of places */

int dist[2*MAXS]; /* distance to each place */

/* heap data structure */
int heap[2*MAXS]; 
int hsize;
int hloc[2*MAXS]; /* location within heap of each place */

/* debugging routine */
/* ensure heap order has been maintained */
void check_heap(void)
 {
  int lv;

  for (lv = 1; lv < hsize; lv++)
   {
    if (dist[heap[lv]] < dist[heap[(lv-1)/2]])
     {
      fprintf (stderr, "HEAP ERROR!\n");
      return;
     }
   }
 }

/* delete the minimum item from the heap */
void delete_min(void)
 {
  int loc, val;
  int p, t;

  /* remove last item in the heap array */
  loc = heap[--hsize];
  val = dist[loc];
  p = 0;

  while (2*p+1 < hsize)
   {
    /* find smaller child */
    t = 2*p+1;
    if (t+1 < hsize && dist[heap[t+1]] < dist[heap[t]]) t++;

    /* if child less than the removed item, move child up */
    if (dist[heap[t]] < val)
     {
      heap[p] = heap[t];
      hloc[heap[p]] = p;
      p = t;
     } else break; /* otherwise, put removed item here */
   }

  /* put removed item back into the heap */
  heap[p] = loc;
  hloc[loc] = p;
  check_heap(); /* sanity check */
 }

void update(int loc)
 { /* we've decreased the value of dist[loc] */
   /* change heap to maintain heap order */
  int val;
  int p, t;

  val = dist[loc];
  p = hloc[loc];
  while (p > 0)
   { /* while the element's not the root of the heap */

    /* check to see if it's less than it's parent */
    t = (p-1)/2;
    if (dist[heap[t]] > val)
     { /* if so, move parent down */
      heap[p] = heap[t];
      hloc[heap[p]] = p;

      /* consider as if updated element is now in parent's prev location */
      p = t;
     } else break; /* otherwise, stop */
   }

  /* put element in proper location in heap */
  heap[p] = loc;
  hloc[loc] = p;
  check_heap(); /* sanity check */
 }

void add_heap(int loc)
 { /* add an element to the heap /*

  if (hloc[loc] == -1)
   { /* if it's not in the heap, add to the end (eff value = infinity) */
    heap[hsize++] = loc;
    hloc[loc] = hsize-1;
   }

  /* change the value to the real value */
  update(loc);
 }


void fill_dist(int s)
 {
  int lv;
  int p;
  int t;
  int l;

 /* initialize hloc & dist */
  for (lv = 0; lv < npl; lv++) { hloc[lv] = -1; dist[lv] = 255*MAXS + 1; }
  dist[s] = 0;
  hsize = 0;
  add_heap(s);

  while (hsize)
   { /* heap is not empty */

    /* take minimum distance location */
    p = heap[0];
    delete_min();
    t = dist[p];

    /* try all possible endpoints of other edges at the same location */
    for (lv = 0; lv < ccnt[p]; lv++)
     {
      l = alist[p][lv];
      if (dist[l] > t)
       { /* found better distance */
        dist[l] = t;
	add_heap(l); /* add, if necessary, update otherwise */
       }
     }

    /* consider moving across this edge */
    t = dist[p] + length[p/2]; 
    p = p ^ 0x1; /* go to the other endpoint */
    if (dist[p] > t) 
     { /* found a better way to get to the location */
      dist[p] = t;
      add_heap(p);
     }
   }
 }

int main(int argc, char **argv)
 {
  FILE *fout, *fin;
  int lv, lv2, lv3;
  int c1, c2;
  int segid, len;
  int p;
  int min;

  if ((fin = fopen("fence6.in", "r")) == NULL)
   {
    perror ("fopen fin");
    exit(1);
   }
  if ((fout = fopen("fence6.out", "w")) == NULL)
   {
    perror ("fopen fout");
    exit(1);
   }

  fscanf (fin, "%d\n", &nfence);
  npl = nfence*2;

  for (lv = 0; lv < nfence; lv++)
   { /* read in edges */
    fscanf (fin, "%d %d %d %d", &segid, &len, &c1, &c2);
    segid--;
    length[segid] = len;
    ccnt[2*segid] = c1;
    ccnt[2*segid+1] = c2;
    while (c1--)
     {
      fscanf (fin, "%d", &p);
      conn[2*segid][c1] = p-1;
     }
    while (c2--)
     {
      fscanf (fin, "%d", &p);
      conn[2*segid+1][c2] = p-1;
     }
   }

  for (lv = 0; lv < npl; lv++)
    for (lv2 = 0; lv2 < ccnt[lv]; lv2++)
     { /* for all edges */
      c1 = lv/2;
      c2 = conn[lv][lv2]*2;

      /* find other occurance of edge */
      for (lv3 = 0; lv3 < ccnt[c2]; lv3++)
        if (conn[c2][lv3] == c1)
	  break;

      /* if no match was found, must be on 'other' side of edge */
      if (lv3 >= ccnt[c2]) c2++;

      /* update adjaceny list */
      alist[lv][lv2] = c2;
     }

  min = 255*MAXS+1; /* higher than we could ever see */

  for (lv = 0; lv < nfence; lv++)
   { /* for each fence */
     
    /* make edge infinite length, to ensure it's not used */
    len = length[lv];
    length[lv] = 255*MAXS+1; 

    /* find distance from one end-point of edge to the other */
    fill_dist(2*lv);

    /* if the cycle (the path plus the edge deleted) is better
       than the best found so far, update min */
    if (dist[2*lv+1] + len < min)
      min = dist[2*lv+1] + len; 

    /* put edge back in */
    /* actually, not necessary, since we've already found the
       best cycle which uses this edge */
    length[lv] = len;
   }

  fprintf (fout, "%i\n", min);
  return 0;
 }

Bjarke Roune has some improvements:

When creating the graph representation, instead of searching through all edges to identy the nodes that corresponds to the endspoints of the edges (fence segments), a signature of each node is created. This signature consists of the two maximal IDs of the edges (fence segments) that connect at that node. These IDs are guarantueed to be unique, as the fence segments are straight, so if two fence segments meet at one point, they cannot meet anywhere else (the fence segments cannot be right on top of each other, as the problem statement says they only meet at their endpoints).

When running the Dijkstra algorithm, it is unnecessary to consider any paths anywhere that are equal to or greater than the smallest perimeter found so far. These paths can safely be considered of infinite distance (i.e., they can be ignored).

After having found the smallest perimeter that includes a certain node, that node can safely be removed from the graph, since we have already got the smallest perimeter that includes that node. (a comment in the solution also says this, but for some reason the node is put back in anyway).

The solution below is blazingly fast...RK

 
 
#include <fstream.h>

const int Inf = 1000 * 1000 * 1000;

ifstream in ("fence6.in");
ofstream out ("fence6.out");

/* The edges are one-way so two edges are neccesary for each fence segment. */

struct Edge {
    Edge () { }
    Edge (int _to, int _dist):to (_to), dist (_dist) { }
    int     to, dist;
}       edges[100][8];		/* edges[x][y] = edge number y of node
				   number x */
int     edgeCo[100];		/* edgeCo[x]   = number of edges that
				   connect at node x */
int     nodeCo = 0;		/* total number of nodes */

/*
 * Each node is given a signature consisting of the two largest fence
 * segment IDs of all the fence segments that connect at that node
 */

int     sigs[100][2];		/* sigs[x] = signature of node x.
				   sigs[x][0] > sigs[x][1] */

void 
AddEdge (int from, const Edge & edge)
{
    edges[from][edgeCo[from]++] = edge;
}

/* Returns the removed edge */
Edge 
RemoveEdge (int from, int to)
{
    int     i;
    for (i = 0; i < edgeCo[from]; ++i) {
	if (edges[from][i].to == to) {
	    Edge    tmp = edges[from][i];
	    edges[from][i] = edges[from][--edgeCo[from]];
	    return tmp;
	}
    }
}

/*
 * The segs parameter is an array of length len that contains the IDs of the
 * fence segments that meet at the sought-for node.
 */

int 
GetNode (int *segs, int len) {
 /* The signature of the node is calculated by retriving the two largets
    fence segment IDs */
    int     i, max0 = 0, max1 = 0;
    for (i = 0; i < len; ++i) {
	if (segs[i] > max1) {
	    if (segs[i] > max0) {
		max1 = max0;
		max0 = segs[i];
	    }
	    else
		max1 = segs[i];
	}
    }

 /* The known signatures are searched. If a matching signature is found,
    the corresponding node is returned */
    for (i = 0; i < nodeCo; ++i)
	if (max0 == sigs[i][0] && max1 == sigs[i][1])
	    return i;

 /* A signature could not be made, so we will have to create a new node
    with that signature. This node is then returned */
    sigs[nodeCo][0] = max0;
    sigs[nodeCo][1] = max1;

    return nodeCo++;
}

/* This function returns length of the shortest path between the passed-in
   nodes.
   Paths of length equal to or larger than lessThan are not considered.
   It is not guaranteed that the graph is connected.
   If no suitable path is found, Infinity is returned
*/
int 
CalcShortestPath (int from, int to, int lessThan) {
    bool    visited[100];
    int     dist[100];

    int     i;
    for (i = 0; i < nodeCo; ++i)
	visited[i] = false, dist[i] = Inf;

    visited[from] = true;
    dist[from] = 0;
    int     lastVisited = from;

    for (;;) {
	int     j;
	for (j = 0; j < edgeCo[lastVisited]; ++j) {
	    const   Edge & edge = edges[lastVisited][j];
	    int     d = dist[lastVisited] + edge.dist;
	    if (d < lessThan && dist[edge.to] > d)
		dist[edge.to] = d;
	}

	int     minDist = Inf;
	for (j = 0; j < nodeCo; ++j)
	    if (!visited[j] && minDist > dist[j])
		lastVisited = j, minDist = dist[j];

	if (minDist == Inf)
	    break;

	visited[lastVisited] = true;
    }

    return dist[to];
}

void 
ReadArray (int *arr, int len, istream & in)
{
    int     i;
    for (i = 0; i < len; ++i)
	in >> arr[i];
}

int 
main () {
    int     segCo;
    in >> segCo;

    int     i;
    for (i = 0; i < segCo; ++i) {
	int     dist, co1, co2, segs[9];
	in >> segs[0] >> dist >> co1 >> co2;

	ReadArray (segs + 1, co1, in);
	int     node1 = GetNode (segs, co1 + 1);

	ReadArray (segs + 1, co2, in);
	int     node2 = GetNode (segs, co2 + 1);

	AddEdge (node1, Edge (node2, dist));
	AddEdge (node2, Edge (node1, dist));
    }

 /*
  * For each node, the smallest perimeter that includes that node is
  * calculated. After each calculation, that node is deleted, as no smaller
  * perimeter can possibly include that node (we already got the smallest
  * one).
  */
    int     minPerim = Inf;
    while (nodeCo != 0) {
	int     node = nodeCo - 1;

    /* Nodes and therefore edges are continually removed, so there might be
       some nodes with less than 2 edges. Such nodes obviously cannot be
       part of any perimeter. */
	if (edgeCo[node] > 1) {
	/* The minimal perimeter that includes the node is calculated */
	    for (i = 0; i < edgeCo[node]; ++i) {
		int     otherNode = edges[node][i].to;

		Edge    out = RemoveEdge (node, otherNode);
		Edge    in = RemoveEdge (otherNode, node);

		int     perim = out.dist + CalcShortestPath (node,
				        otherNode, minPerim - out.dist);

		if (minPerim > perim)
		    minPerim = perim;

		AddEdge (node, out);
		AddEdge (otherNode, in);
	    }
	}

    /* The node is deleted by deleting all edges to it and decreasing
       nodeCo, so that the current node "falls of the edge" of the now
       smaller edges array */
	for (i = 0; i < edgeCo[node]; ++i)
	    RemoveEdge (edges[node][i].to, node);
	--nodeCo;
    }

    out << minPerim << '\n';

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值