USACO Fence Loops 解题报告

这道题是看了网上的思路做出来的。之前尝试过(最小)生成树的方法,这似乎是获取所有“基本环”的标准方法,时间复杂度也更低,但由于我不知道怎么证明加之懒惰,没有这样实现。

这道题的思路是:逐一去掉一条边(把这条边的length设为无穷大),然后求从这条边的左端点到右端点的最短路径(使用Dijkstra),这样通过该边的最小基本环的长度就是这条边的长度与最短路径之和。所有的边遍历完后,最小基本环中最小的即为所求。由于对每条边都要运行一次Dijkstra,所以时间复杂度为O(E^2logV). 结题报告中有优化方法,大意似乎是去掉长于当前最小基本环的环。不过也懒得细看了。。。

这道题值得总结的地方是Dijkstra的实现。之前也实现过,不过确实感到很复杂,heap的操作都要自己写一遍。这里附上标准结题报告,算是Dijkstra标准程序吧。我自己的实现为了简单,使用了map,时间复杂度变成了EV。

/* 
ID: thestor1 
LANG: C++ 
TASK: fence6 
*/
#include <iostream>  
#include <cmath>  
#include <cstdio>  
#include <cstring>  
#include <climits>  
#include <cassert>  
#include <string>  
#include <vector>  
#include <set>
#include <map>  
#include <queue>  
#include <stack>  
#include <algorithm>

using namespace std;

const int INF = 30000;

bool cmp(const pair<int, int> &lhs, const pair<int, int> &rhs)
{
	return lhs.second > rhs.second;
}

int dijkstra(int s, const int N, vector<int> &length, const vector<set<int> > &ladj, const vector<set<int> > &radj)
{
	int ls = length[s];
	length[s] = INF;

	map<int, int> dis;
	for(int i = 0; i < N; ++i)
	{
		dis[i] = INF;
	}

	for(set<int>::iterator iter = ladj[s].begin(); iter != ladj[s].end(); ++iter)
	{
		dis[*iter] = length[*iter];
	}
	
	while(dis.size())
	{
		// cout<<"dis:";
		// for(map<int, int>::iterator iter = dis.begin(); iter != dis.end(); ++iter)
		// {
		// 	cout<<iter->first<<", "<<iter->second<<"\t";
		// }
		// cout<<endl;

		map<int, int>::iterator uiter = dis.begin();
		for(map<int, int>::iterator iter = ++dis.begin(); iter != dis.end(); ++iter)
		{
			if(iter->second < uiter->second)
			{
				uiter = iter;
			}
		}

		int u = uiter->first, du = uiter->second;
		// cout<<"u:"<<u<<", du:"<<du<<endl;
		// int size = dis.size();
		dis.erase(uiter);
		// assert(dis.size() == size - 1);

		if(radj[s].find(u) != radj[s].end() || du == INF)
		{
			length[s] = ls;
			return du;
		}

		for(map<int, int>::iterator iter = dis.begin(); iter != dis.end(); ++iter)
		{
			if((ladj[u].find(iter->first) != ladj[u].end() || radj[u].find(iter->first) != radj[u].end()) && iter->second > du + length[iter->first])
			{
				iter->second = du + length[iter->first];
			}
		}
	}
	length[s] = ls;
	return INF;
}

int main()
{
	FILE *fin  = fopen("fence6.in", "r");
	FILE *fout = fopen("fence6.out", "w");
	int N;
	fscanf(fin, "%d", &N);
	// cout<<"N: "<<N<<endl;
	vector<int> length(N);
	vector<set<int> > ladj(N);
	vector<set<int> > radj(N);
	for(int i = 0; i < N; ++i)
	{
		int s, l, n1, n2;
		fscanf(fin, "%d%d%d%d", &s, &l, &n1, &n2);
		length[s - 1] = l;
		for(int j = 0; j < n1; ++j)
		{
			int k;
			fscanf(fin, "%d", &k);
			ladj[s - 1].insert(k - 1);
		}
		for(int j = 0; j < n2; ++j)
		{
			int k;
			fscanf(fin, "%d", &k);
			radj[s - 1].insert(k - 1);
		}
	}

	// cout<<"ladj[0]:";
	// for(set<int>::iterator iter = ladj[0].begin(); iter != ladj[0].end(); ++iter)
	// {
	// 	cout<<*iter<<"\t";
	// }
	// cout<<endl;

	// cout<<"radj[0]:";
	// for(set<int>::iterator iter = radj[0].begin(); iter != radj[0].end(); ++iter)
	// {
	// 	cout<<*iter<<"\t";
	// }
	// cout<<endl;

	int mincycle = -1, mini = -1;
	for(int i = 0; i < N; ++i)
	{
		int thiscycle = length[i] + dijkstra(i, N, length, ladj, radj);
		if(mincycle < 0 || thiscycle < mincycle)
		{
			mincycle = thiscycle;
			mini = i;
		}
	}

	// cout<<"[main]mincycle:"<<mincycle<<", mini:"<<mini<<", length[i]:"<<length[mini]<<", dijkstra:"<<dijkstra(mini, N, length, ladj, radj)<<endl;
	fprintf(fout, "%d\n", mincycle);

	fclose(fin);
	fclose(fout);
	return 0;  
}

标准程序:

#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;
 }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值