UVa OJ 140 - Bandwidth (帶寬)

Time limit: 3.000 seconds
限時3.000秒html

 

Problem
問題

Given a graph (V,E) where V is a set of nodes and E is a set of arcs in VxV, and an ordering on the elements in V, then the bandwidth of a node v is defined as the maximum distance in the ordering between v and any node to which it is connected in the graph. The bandwidth of the ordering is then defined as the maximum of the individual bandwidths. For example, consider the following graph:
給定一個圖(V,E),其中V爲頂點的集合,E爲邊的集合,屬於VxV。給定V中元素的一種排序,那麼頂點v的帶寬定義以下:在當前給定的排序中,與v距離最遠的且與v有邊相連的頂點與v的距離。給定排序的帶寬定義爲各頂點帶寬的最大值。例如考慮以下圖:
node

 

140img1

 

This can be ordered in many ways, two of which are illustrated below:
此圖能夠給出多種排序,其中兩個排序圖示以下:ios

 

140img2

 

For these orderings, the bandwidths of the nodes (in order) are 6, 6, 1, 4, 1, 1, 6, 6 giving an ordering bandwidth of 6, and 5, 3, 1, 4, 3, 5, 1, 4 giving an ordering bandwidth of 5.
對於給出的這兩個排序,它們各結點的帶寬分別是(按排序順序):6, 6, 1, 4, 1, 1, 6, 6,排序帶寬爲6,以及5, 3, 1, 4, 3, 5, 1, 4,排序帶寬爲5。

Write a program that will find the ordering of a graph that minimises the bandwidth.
寫一個程序,找出該圖的一種排序使其帶寬最小。算法

 

Input
輸入

Input will consist of a series of graphs. Each graph will appear on a line by itself. The entire file will be terminated by a line consisting of a single#. For each graph, the input will consist of a series of records separated by `;'. Each record will consist of a node name (a single upper case character in the the range `A' to `Z'),followed by a `:' and at least one of its neighbours. The graph will contain no more than 8 nodes.
輸入由一系列的圖構成。每一個圖獨佔一行。一個僅包含「#」字符的一行輸入標誌整個輸入文件結束。對於每一個圖的輸入,都包含一系列由「;」隔開的記錄。每一個記錄包含一個結點名(一個大寫字母,範圍是「A」到「Z」),接着是一個「:」,而後是一些該結點的鄰居結點。圖中不會包含超過8個結點。數組

 

Output
輸出

Output will consist of one line for each graph, listing the ordering of the nodes followed by an arrow (->) and the bandwidth for that ordering. All items must be separated from their neighbours by exactly one space. If more than one ordering produces the same bandwidth, then choose the smallest in lexicographic ordering, that is the one that would appear first in an alphabetic listing.
每一個圖對應一行輸出,列出排序的結點,而後是一個箭頭(->)以及該排序的帶寬值。全部項都應由一個空格與它相鄰的項隔開。若是同一個帶寬有多種排序方法,取字母序最小的一種排序,也就是取字母表排在前面的一種排序。數據結構

 

Sample input
示例輸入

A:FB;B:GC;D:GC;F:AGH;E:HD
#
app

 

Analysis
分析

典型的優化問題,一個可行解就是一個排序,目標函數就是解的帶寬。但ACM的優化問題必定是使用肯定性算法的(與包含隨機元素的現代優化算法如遺傳、粒子相對),肯定性優化算法就那麼幾種,貪心、動態規劃、單純行(解線性規劃)還有就是暴力。注意到題中專門強調:圖不會超過8個結點,結點的名字都是從「A」到「Z」,相同帶寬的排序取字典序靠前等,全部的線索都指向了一種算法:暴力搜索。ide

從要求的「字典序」獲得啓發:全排列生成算法。剩下的問題就是以何種數據結構來存儲,以便快速的查找頂點和計算帶寬。對於一種頂點的排序,要查找指定頂點所在的位置,最快的方法就是使用標記數組。因爲題中限定的頂點的取值範轉:「A」到「Z」,所以用一個26個元素的數組便可記錄每一個頂點在排序中的位置。函數

記下頂點的位置後,須要用最快的方式查出每一個頂點到它的鄰居的距離,這實際就是在當前排序中找出相距最遠的一條邊。所以只要遍歷圖中全部的邊,計算其在當前排序中的距離,並記錄最遠的距離便可。圖能夠用邊的集合來表示圖,每一個邊用兩個頂點來表示。因爲是無向圖,所以可以使邊的兩個頂點程左小右大排序,而後保證無重邊便可。優化

 

Solution
解答

 

#include "stdafx.h"
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>

typedef std::pair<char, char> NODEPAIR;
typedef std::vector<char>::iterator NODE_ITER;
typedef std::string::iterator STR_ITER;
typedef std::vector<NODEPAIR>::iterator GRAPH_ITER;

int main(void)
{
	char idxTbl[32]; // 從頂點編號到其在某種排序中的位置的對應表
	std::string strLine; // 存儲一行輸入的字符串
	for (; std::getline(std::cin, strLine) && strLine[0] != '#'; ) {
		std::vector<NODEPAIR> graph; // 數組中每一個元素爲一條邊,用一對頂點的編號表示
		std::vector<char> nodes; // 記錄一種頂點的排序
		// 爲方便判斷一行輸入的結束,在行層添加分號
		strLine.push_back(';');
		// 循環處理當前輸入行的每個字符
		for (STR_ITER i = strLine.begin(); i != strLine.end(); ++i) {
			// 用全部邊的頂點對來表示圖,nFrom是冒號前的頂點,nTo是冒號後的頂點
			char nFrom = *i - 'A'; // 每一個頂點的編號爲其字母的ASCII碼-'A'
			// 有些頂點只出如今冒號前,有些只出如今冒號後,所以nFrom和nTo都需添加
			nodes.push_back(nFrom);
			// 遍歷冒號後面直到分號的頂點,這些頂點用nTo表示
			for (i += 2; *i != ';'; ++i) {
				char nTo = *i - 'A';  // 每一個頂點的編號爲其字母的ASCII碼-'A'
				nodes.push_back(nTo);
				// 保證添加到圖中的頂點對(邊)中編號較小的頂點在前,避免無向圖的重複邊
				if (nFrom > nTo)
					graph.push_back(NODEPAIR(nFrom, nTo));
				else if (nFrom < nTo)
					graph.push_back(NODEPAIR(nTo, nFrom));
			}
		}
		// 對圖的全部頂點對(邊)排序去重
		std::sort(graph.begin(), graph.end());
		graph.erase(std::unique(graph.begin(), graph.end()), graph.end());
		// 對頂點數組排序去重,做爲第一種排序(升序最小)
		std::sort(nodes.begin(), nodes.end());
		nodes.erase(std::unique(nodes.begin(), nodes.end()), nodes.end());
		std::vector<char> minOrder; // 記錄具備最小帶寬的排序
		char nMinBw = char(nodes.size()); // 記錄具備最小的帶寬,初始化爲數組長度
		for (bool bNext = true; bNext; ) { // 遍歷全部排序
			char nCnt = 0, nOrderBw = 0;
			// 掃描一遍當前的排序,求出每個頂點在當前排序中的位置
			for (NODE_ITER i = nodes.begin(); i != nodes.end(); ++i) {
				idxTbl[*i] = nCnt++;
			}
			// 遍歷圖中的全部頂點對,找出在當前排序中距離最遠的頂點對做爲當前排序的帶寬
			for (GRAPH_ITER i = graph.begin(); i != graph.end(); ++i) {
				char nCur = char(std::abs(idxTbl[i->first] - idxTbl[i->second]));
				if (nCur > nOrderBw)
					nOrderBw = nCur;
			}
			// 若是當前排序的帶寬小於已知最小帶寬,則更新最小帶寬和最小排序
			if (nOrderBw < nMinBw) {
				nMinBw = nOrderBw;
				minOrder = nodes;
			}
			// 接字母序給出下一種排序
			bNext = std::next_permutation(nodes.begin(), nodes.end());
		}
		// 按格式循環輸出最小排序和它的帶寬值
		for (NODE_ITER i = minOrder.begin(); i != minOrder.end(); ++i) {
			std::cout << char(*i + 'A') << ' ';
		}
		std::cout << "-> " << (int)nMinBw << std::endl;
	}
	return 0;
}
相關文章
相關標籤/搜索