Posts Tagged ‘ACM’

Sicily 1031 Campus 解题报告.

 

1 原题中文大意;
 
Sicily 1031 Campus实际上就是求一个图中给出两点的最短路径的问题。
 
2 算法思想及解题用到的主要数据结构;
 
用 dijkstra 算法求给定两点的最短路径.
 
按路径长度递增次序产生最短路径算法:   把V分成两组:   (1)S:已求出最短路径的顶点的集合   (2)V-S=T:尚未确定最短路径的顶点集合   将T中顶点按最短路径递增的次序加入到S中,   保证:(1)从源点V0到S中各顶点的最短路径长度都不大于   从V0到T中任何顶点的最短路径长度   (2)每个顶点对应一个距离值   S中顶点:从V0到此顶点的最短路径长度   T中顶点:从V0到此顶点的只包括S中顶点作中间   顶点的最短路径长度   依据:可以证明V0到T中顶点Vk的最短路径,或是从V0到Vk的   直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和。
 
3  逐步求精算法描述
 
用邻接矩阵存放图.1. 初使时令 S={V0},T={其余顶点},T中顶点对应的距离值   若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值   若不存在<V0,Vi>,d(V0,Vi)为∝   2. 从T中选取一个其距离值为最小的顶点W且不在S中,加入S   3. 对T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的   距离值比不加W的路径要短,则修改此距离值   重复上述步骤2、3,直到S中包含所有顶点,即S=T为止
 
3 程序注释清单 // 这份代码通不过sicily。有case错误。我实在找不到原因。
 
#include <map>

#include <vector>

#include <string>

#include <iostream>

#include <cstdlib>

#include <cstdio>

#include <cstring>

#include <cassert>



#define INFINITE 0x0FFFFFFF



// 这个类的函数几乎不做参数有效性的检查. 

class graph_t

{

public:

// 构建一个 num x num 的邻接矩阵

graph_t(int num)

{

m_matrix = new int[num*num];

m_sunit = new bool[num];

m_spath_table = new int[num];

// memset(m_matrix, INFINITE, num*num);  // INFINITE 表示无穷远. 

for ( int i = 0; i < num*num; i++ )

{

m_matrix[i] = INFINITE;

}

m_num = num;

}



// row,column 取 [0, m_num)

int& operator()(int row, int column)

{

return m_matrix[row * m_num + column];

}



// vtx_start, vtx_end 取 [vtx_start, vtx_end)

int shortestpath_dij(int vtx_start, int vtx_end)

{

init_dij(vtx_start);  // 初始化dijkstra算法需要的一些数据. 



if ( vtx_end == vtx_start )

{

return this->operator()(vtx_start, vtx_end);

}



// 还剩 m_num - 1 个点. 

for ( int i = 1; i < m_num; i++ )

{

// 找下一个最近的点. 

int vtx = -1, min = INFINITE;

for ( int j = 0; j < m_num; j++ )

{

if ( m_sunit[j] )  // 这个点已经在已经求得最短路径的点的集合中了. 

continue;



if ( m_spath_table[j] < min )

{

vtx = j;

min = m_spath_table[j];

}

}

// 已经没有连通的顶点了. 

if ( vtx == -1 )

{

return -1;

}

m_sunit[vtx] = true;  // 把这个点加入集合中. 



// 这个点是终点.  http://blog.ykyi.net

if ( vtx == vtx_end )

{

return min;

}



// 因为找到了一个新的点加入了集合,更新最短路径表. 

for ( int k = 0; k < m_num; k++ )

{

if ( m_sunit[k] )

continue;



if (  min + this->operator()(vtx, k) < m_spath_table[k] )

{

m_spath_table[k] = min + this->operator()(vtx, k);

}

}

}



assert(0); // it's not suppossed to reach here.

return -1;

}



~graph_t()

{

delete m_matrix;

delete m_sunit;

delete m_spath_table;

}



private:

void init_dij(int vtx_start)

{

memset(m_sunit, 0, m_num);   // 初始化为没有点加入这个已求得最短路径的终点集合.

for ( int i = 0; i < m_num; i++ )

{

m_spath_table[i] = this->operator()(vtx_start, i);

}

assert( 0 == m_spath_table[vtx_start] );

m_sunit[vtx_start] = true;

}



private:

int   m_num;

int*  m_matrix;  // 存邻接矩阵

bool* m_sunit;   // 在dijkstra计算过程中用来存某点是否已经是最短路径中的点.

int*  m_spath_table;  // 在dijkstra计算过程中存到某点是的最短路径是多少.

};





int main(int argc, char* argv[])

{

int case_count, path_count, weight, next_ava_vtx;

int vtx_start, vtx_end;

std::map<std::string, int> loc2vtx;  // 存地点字符串到图的顶点号的映射. 

std::vector<int> spirit;

std::string line;



std::cin >> case_count;

for ( int i = 0; i < case_count; i++ )

{

loc2vtx.clear();

spirit.clear();

next_ava_vtx = 0;



std::cin >> path_count;

for ( int j = 0; j < path_count; j++ )

{

// 得到起点地址字符串. 

std::cin >> line;

std::map<std::string, int>::iterator ite = loc2vtx.find(line);

if ( loc2vtx.end() != ite )

{

vtx_start = ite->second;

}

else

{

vtx_start = next_ava_vtx;

loc2vtx[line] = vtx_start;

next_ava_vtx++;

}

spirit.push_back(vtx_start);



// 得到终点地址字符串. 

std::cin >> line;

ite = loc2vtx.find(line);

if ( loc2vtx.end() != ite )

{

vtx_end = ite->second;

}

else

{

vtx_end = next_ava_vtx;

loc2vtx[line] = vtx_end;

next_ava_vtx++;

}

spirit.push_back(vtx_end);



// 得到权重

std::cin >> weight;

spirit.push_back(weight);

}



// 至此 next_ava_vtx 中存放的实际上是邻接方阵的阶. 

graph_t graph(next_ava_vtx);

for ( int i = 0; i < spirit.size()/3; i++ )

{

int x = spirit[3*i];

int y = spirit[3*i+1];

int w = spirit[3*i+2];

graph(x, y) = w;

graph(y, x) = w;

}

for ( int i = 0; i < next_ava_vtx; i++ )

{

graph(i, i) = 0;

}



std::cin >> line;

std::map<std::string, int>::iterator ite = loc2vtx.find(line);

if ( ite == loc2vtx.end() )

{

std::cout << "-1\n";

continue;

}

vtx_start = loc2vtx[line];



std::cin >> line;

ite = loc2vtx.find(line);

if ( ite == loc2vtx.end() )

{

std::cout << "-1\n";

continue;

}

vtx_end = loc2vtx[line];



if ( vtx_start == vtx_end )

{

std::cout << "0\n";

continue;

}



std::cout << graph.shortestpath_dij(vtx_start, vtx_end) << std::endl;

}



return 0;

}

 

 
4 对时间复杂度,空间复杂度方面的分析、估算。
 
时间复杂度和空间复杂度都是O(n*n).   http://blog.ykyi.net
 
///////////// 原题
 
 
 
 
 
1031. Campus
 
 
 
 
 
Description
 
At present, Zhongshan University has 4 campuses with a total area of 6.17 square kilometers sitting respectively on both sides of the Pearl River or facing the South China Sea. The Guangzhou South Campus covers an area of 1.17 square kilometers, the North Campus covers an area of 0.39 square kilometers, the Guangzhou East Campus has an area of 1.13 square kilometers and the Zhuhai Campus covers an area of 3.48 square kilometers. All campuses have exuberance of green trees, abundance of lawns and beautiful sceneries, and are ideal for molding the temperaments, studying and doing research.
 
 
 
 
       Sometime, the professors and students have to go from one place to another place in one campus or between campuses. They want to find the shortest path between their source place S and target place T. Can you help them?
 
 
Input
 
The first line of the input is a positive integer C. C is the number of test cases followed. In each test case, the first line is a positive integer N (0<N<=100) that represents the number of roads. After that, N lines follow. The i-th(1<=i<=N) line contains two strings Si, Ti and one integer Di (0<=Di<=100). It means that there is a road whose length is Di between Si and Ti. Finally, there are two strings S and T, you have to find the shortest path between S and T. S, T, Si(1<=i<=N) and Ti(1<=i<=N) are all given in the following format: str_Campus.str_Place. str_Campus represents the name of the campus, and str_Place represents the place in str_Campus. str_Campus is "North", "South", "East" or "Zhuhai". str_Place is a string which has less than one hundred lowercase characters from "a-z". You can assume that there is at most one road directly between any two places.
 
Output
 
The output of the program should consist of C lines, one line for each test case. For each test case, the output is a single line containing one integer. If there is a path between S and T, output the length of the shortest path between them. Otherwise just output "-1" (without quotation mark). No redundant spaces are needed.
 
 

 

Be the first to comment - What do you think?  Posted by zausiu - December 26, 2011 at 13:12

Categories: Tech Articles   Tags: ,

ACM 马周游解题报告

 

 

 

简单的马周游问题

 

 

 

 
Description
 
在一个5 * 6的棋盘中的某个位置有一只马,如果它走29步正好经过除起点外的其他位置各一次,这样一种走法则称马的周游路线,试设计一个算法,从给定的起点出发,找出它的一条周游路线。
 
为了便于表示一个棋盘,我们按照从上到下,从左到右对棋盘的方格编号,如下所示:
 
1     2     3       4     5     6
 
7     8     9       10    11       12
 
13    14       15    16       17    18
 
19    20       21    22       23    24
 
25    26       27    28       29    30
 
马的走法是“日”字形路线,例如当马在位置15的时候,它可以到达2、4、7、11、19、23、26和28。但是规定马是不能跳出棋盘外的,例如从位置1只能到达9和14。
 
Input
 
输入有若干行。每行一个整数N(1<=N<=30),表示马的起点。最后一行用-1表示结束,不用处理。
 
Output
 
对输入的每一个起点,求一条周游线路。对应地输出一行,有30个整数,从起点开始按顺序给出马每次经过的棋盘方格的编号。相邻的数字用一个空格分开。
 
Sample Input
 
4
-1
Sample Output
 
注意:如果起点和输入给定的不同,重复多次经过同一方格或者有的方格没有被经过,都会被认为是错误的。
 
 
(1)原题中文大意
 
中国象棋的马按照象棋的规则在5 x 6 的棋盘上跑30步跑完所有的格子。
 
 
 
(2)算法思想及解题用到的主要数据结构
 
 
 
从每一个点往下走第二步,会有几点有效的行走位置。 把当前位置记在栈里面,以深度优先的方式进入下一个有效位置。如果下一个有效位置在之前已经走过,则从栈顶弹出上一位置,恢复到调出的位置后尝试未走过的有效位置。利用函数调用时会压栈的特别,用函数递归调用即可解决问题。
 
 
 
(3)详细解题思路
 
1. 先初始化一张表。这张表记下了棋盘上所有的30个位置的下一步可走的有效位置.
 
2. 写一个一般的递归调用自己的函数,表示马在走第几步时到了哪个位置,然后尝试走余下的所有可走位置。
 
3. 递归函数有两个退出条件。要么下一个要尝试的点已经走过了,要么走到了最后一步,即得解!
 
 
 
(4)逐步求精算法描述
 
用一个数组记下来经过的点。
 
 
 
(5)程序注释清单
 
#include <iostream>

#include <cstring>

#include <cstdlib>

#include <vector>

using namespace std;

const static char row_num = 5;   // 5行

const static char column_num = 6;  // 6列



// 这个全局数组在初始化后会记下每30个有效位置的下一步可走位置.

// elocution[x][0]记个数。最多8个有效可走位置。

char elocution[30][9];

struct coordinate  // 坐标.

{

char _x;  // start from zero.

char _y;

};

// 坐标转序号的函数。

char coordinate2serial_num(coordinate co)

{

char num = co._x * column_num + co._y + 1;

return num;

}

// 序号转坐标的函数。

coordinate serial_num2coordinate(char sn)

{

coordinate co;

co._x = (sn - 1) / column_num;

co._y = sn - co._x * column_num - 1;

return co;

}

// 增量数组.

char increments[8][2] =

{

1, -2, 2, -1, 2, 1, 1, 2,

-1, 2, -2, 1, -2, -1, -1, -2

};

// 得用增量数组来计算每一个位置的下一步有效位置。记到steps里面.

void next_step(char pos, char steps[])

{

char valid_count = 0;

coordinate co = serial_num2coordinate(pos);

coordinate tmp;

char serial_num;

for ( int i = 0; i < 8; i++ )

{

tmp._x = co._x + increments[i][0];

tmp._y = co._y + increments[i][1];

if ( tmp._x < 0 || tmp._x > 4 || tmp._y < 0 || tmp._y > 5 )

{

continue;

}

serial_num = coordinate2serial_num(tmp);

if ( serial_num >= 1 && serial_num <= 30 )

{

valid_count++;

steps[valid_count] = serial_num;

}

else

{

cerr << "Not expected to reach here.\n";

}

}

steps[0] = valid_count;

}

// 初始化记载所有点的下一步可走点的全局表。

void init_elocution()

{

for ( int i = 1; i <= 30; i++ )

{

next_step(i, elocution[i-1]);

}

}



char track[30];   // 记走过的路径.

bool quit_run_horse;

// 上文提高的递归函用函数.

void run_horse(char start_pos, char step_count)

{

if ( quit_run_horse ) // 用来快速退出深嵌套递归调用函数.

{

return;

}

for ( int i = 0; i < step_count; i++ )

{

// 已?经-经-过y这a点?了?.

if ( track[i] == start_pos )

{

return;

}

}

track[step_count] = start_pos; // 记下新走过的位置



if ( step_count == 29 )  // 最后一步则打印出结果。

{

for ( int i = 0; i < sizeof(track); i++ )

{

cout << (int)track[i];

if ( i + 1 != sizeof(track) )

{

cout << " ";

}

}

cout << endl;

quit_run_horse = true;

return;

}



for ( int i = 0; i < elocution[start_pos-1][0]; i++ )

{

run_horse(elocution[start_pos-1][i+1], step_count+1);

}

}

int main(int argc, char* argv[])

{

init_elocution();

int pos;

vector<int> vec;



while(true)

{

cin >> pos;

if (pos==-1)

{

break;

}

vec.push_back(pos);

}



for ( int i = 0; i < vec.size(); i++ )

{

quit_run_horse = false;

run_horse(vec[i], 0);

}

return 0;

}

 

 
 
 

Be the first to comment - What do you think?  Posted by zausiu - November 25, 2011 at 12:33

Categories: Tech Articles   Tags: